Merge pull request #668 from Shabinder/sound-cloud

Sound cloud Support and More
This commit is contained in:
Shabinder Singh 2021-10-11 15:15:57 +05:30 committed by GitHub
commit c760385727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 1584 additions and 1343 deletions

View File

@ -16,7 +16,6 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import org.jetbrains.compose.compose
import org.jetbrains.kotlin.kapt.cli.main
plugins {
id("com.android.application")
@ -79,6 +78,9 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
packagingOptions {
exclude("META-INF/*")
}
configurations {
"implementation" {
exclude(group = "androidx.compose.animation")
@ -92,7 +94,7 @@ android {
dependencies {
implementation(compose.material)
implementation(compose.materialIconsExtended)
implementation(Androidx.androidxActivity)
implementation(deps.androidx.activity)
// Project's SubModules
implementation(project(":common:database"))
@ -103,43 +105,40 @@ dependencies {
implementation(project(":common:core-components"))
implementation(project(":common:providers"))
// Koin
implementation(Koin.android)
implementation(Koin.compose)
with(deps) {
// DECOMPOSE
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose)
// Koin
with(koin) {
implementation(androidx.compose)
implementation(android)
}
// MVI
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.mvikotlinMain)
implementation(MVIKotlin.mvikotlinLogging)
implementation(MVIKotlin.mvikotlinTimeTravel)
// DECOMPOSE
with(decompose) {
implementation(dep)
implementation(extensions.compose)
}
// Extras
with(Extras.Android) {
implementation(countly)
implementation(appUpdator)
implementation(countly.android)
implementation(android.app.notifier)
implementation(storage.chooser)
with(bundles) {
implementation(ktor)
implementation(mviKotlin)
implementation(androidx.lifecycle)
implementation(accompanist.inset)
}
// Test
testImplementation(junit)
androidTestImplementation(androidx.junit)
androidTestImplementation(androidx.expresso)
// Desugar
coreLibraryDesugaring(androidx.desugar)
// Debug
debugImplementation(leak.canary)
}
with(Versions.androidxLifecycle) {
implementation("androidx.lifecycle:lifecycle-service:$this")
implementation("androidx.lifecycle:lifecycle-common-java8:$this")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$this")
}
implementation(Extras.kermit)
// implementation("com.jakewharton.timber:timber:4.7.1")
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
implementation("com.google.accompanist:accompanist-insets:0.16.1")
// Test
testImplementation("junit:junit:4.13.2")
androidTestImplementation(Androidx.junit)
androidTestImplementation(Androidx.expresso)
// Desugaring
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
}

View File

@ -23,6 +23,7 @@
<package android:name="com.gaana" />
<package android:name="com.spotify.music" />
<package android:name="com.jio.media.jiobeats" />
<package android:name="com.soundcloud.android" />
<package android:name="com.google.android.youtube" />
<package android:name="com.google.android.apps.youtube.music" />
</queries>

View File

@ -43,6 +43,7 @@ import com.shabinder.common.models.event.coroutines.failure
import com.shabinder.common.providers.FetchPlatformQueryResult
import com.shabinder.common.translations.Strings
import com.shabinder.spotiflyer.R
import io.ktor.client.HttpClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
@ -62,6 +63,7 @@ class ForegroundService : LifecycleService() {
private val fetcher: FetchPlatformQueryResult by inject()
private val logger: Kermit by inject()
private val dir: FileManager by inject()
private val httpClient: HttpClient by inject()
private var messageList =
java.util.Collections.synchronizedList(MutableList(5) { emptyMessage })
@ -170,7 +172,7 @@ class ForegroundService : LifecycleService() {
trackStatusFlowMap[track.title] = DownloadStatus.Downloading()
// Enqueueing Download
downloadFile(url).collect {
httpClient.downloadFile(url).collect {
when (it) {
is DownloadResult.Error -> {
logger.d(TAG) { it.message }

View File

@ -15,12 +15,19 @@ fun cleanFiles(dir: File) {
if (file.isDirectory) {
cleanFiles(file)
} else if (file.isFile) {
if (file.path.toString().substringAfterLast(".") != "mp3") {
Log.d("Files Cleaning", "Cleaning ${file.path}")
val filePath = file.path.toString()
if (filePath.substringAfterLast(".") != "mp3" || filePath.isTempFile()) {
Log.d("Files Cleaning", "Cleaning $filePath")
file.delete()
}
}
}
}
} catch (e: Exception) { e.printStackTrace() }
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun String.isTempFile(): Boolean {
return substringBeforeLast(".").takeLast(5) == ".temp"
}

BIN
art/SpotiFlyer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@ -38,22 +38,32 @@ allprojects {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
dependsOn(":common:data-models:generateI18n4kFiles")
kotlinOptions {
if(this is org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions) {
if (this is org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions) {
jvmTarget = "1.8"
}
freeCompilerArgs = (freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn"))
}
}
afterEvaluate {
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
kmpExt.sourceSets.run {
all {
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
configurations.all {
resolutionStrategy {
eachDependency {
if (requested.group == "org.jetbrains.kotlin") {
@Suppress("UnstableApiUsage")
useVersion(deps.kotlin.kotlinGradlePlugin.get().versionConstraint.requiredVersion)
}
removeAll { it.name == "androidAndroidTestRelease" }
}
}
}
afterEvaluate {
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()
?.let { kmpExt ->
kmpExt.sourceSets.run {
all {
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
}
removeAll { it.name == "androidAndroidTestRelease" }
}
}
}
}

View File

@ -30,17 +30,20 @@ repositories {
}
dependencies {
implementation(Androidx.gradlePlugin)
implementation(JetBrains.Compose.gradlePlugin)
implementation(JetBrains.Kotlin.gradlePlugin)
implementation(JetBrains.Kotlin.serialization)
implementation(SqlDelight.gradlePlugin)
implementation(KTLint.gradlePlugin)
implementation(Internationalization.gradlePlugin)
implementation(Mosaic.gradlePlugin)
with(deps) {
implementation(androidx.gradle.plugin)
implementation(kotlin.compose.gradle)
implementation(ktlint.gradle)
implementation(mosaic.gradle)
implementation(kotlin.kotlinGradlePlugin)
implementation(sqldelight.gradle.plugin)
implementation(i18n4k.gradle.plugin)
implementation(kotlin.serialization)
}
}
kotlin {
// Add Deps to compilation, so it will become available in main project
sourceSets.getByName("main").kotlin.srcDir("buildSrc/src/main/kotlin")
}

View File

@ -14,11 +14,15 @@
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("MayBeConstant", "SpellCheckingInspection")
@file:Suppress("MayBeConstant", "SpellCheckingInspection", "UnstableApiUsage")
import org.gradle.api.Project
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
import org.gradle.kotlin.dsl.getByType
object Versions {
// App's Version (To be bumped at each update)
@ -26,44 +30,10 @@ object Versions {
const val versionCode = 26
// Kotlin
const val kotlinVersion = "1.5.21"
const val coroutinesVersion = "1.5.1"
// Code Formatting
const val ktLint = "10.1.0"
// Console-App UI
const val mosaic = "0.1.0"
// DI
const val koin = "3.1.2"
// Logger
const val kermit = "0.1.9"
const val mokoParcelize = "0.7.1"
// Internet
const val ktor = "1.6.2"
const val kotlinxSerialization = "1.2.2"
// Database
const val sqlDelight = "1.5.1"
const val sqliteJdbcDriver = "3.34.0"
const val slf4j = "1.7.31"
// Internationalisation
const val i18n4k = "0.1.3"
// Android
const val minSdkVersion = 21
const val compileSdkVersion = 30
const val compileSdkVersion = 31
const val targetSdkVersion = 29
const val androidxLifecycle = "2.4.0-alpha03"
}
object HostOS {
@ -74,143 +44,46 @@ object HostOS {
val isLinux = hostOs.startsWith("Linux", true)
}
object MultiPlatformSettings {
const val dep = "com.russhwolf:multiplatform-settings-no-arg:0.7.7"
}
val Project.Deps: VersionCatalog get() = project.extensions.getByType<VersionCatalogsExtension>().named("deps")
object KotlinJSWrappers {
private const val bomVersion = "0.0.1-pre.235-kotlin-1.5.21"
val bom = "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${bomVersion}"
const val kotlinReact = "org.jetbrains.kotlin-wrappers:kotlin-react"
const val kotlinReactDom = "org.jetbrains.kotlin-wrappers:kotlin-react-dom"
const val kotlinStyled = "org.jetbrains.kotlin-wrappers:kotlin-styled"
}
val VersionCatalog.ktorBundle get() = findBundle("ktor").get()
val VersionCatalog.statelyBundle get() = findBundle("stately").get()
val VersionCatalog.androidXLifecycleBundle get() = findBundle("androidx-lifecycle").get()
val VersionCatalog.androidXCommonBundle get() = findBundle("androidx-common").get()
val VersionCatalog.kotlinTestBundle get() = findBundle("kotlin-test").get()
val VersionCatalog.sqldelightBundle get() = findBundle("sqldelight").get()
val VersionCatalog.mviKotlinBundle get() = findBundle("mviKotlin").get()
val VersionCatalog.essentyBundle get() = findBundle("essenty").get()
val VersionCatalog.koinAndroidBundle get() = findBundle("koin-android").get()
val VersionCatalog.kotlinJSWrappers get() = findBundle("kotlin-js-wrappers").get()
object Koin {
val core = "io.insert-koin:koin-core:${Versions.koin}"
val test = "io.insert-koin:koin-test:${Versions.koin}"
val android = "io.insert-koin:koin-android:${Versions.koin}"
val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}"
}
val VersionCatalog.kotlinJunitTest get() = findDependency("kotlin-kotlinTestJunit").get()
val VersionCatalog.kotlinJSTest get() = findDependency("kotlin-kotlinTestJs").get()
val VersionCatalog.kermit get() = findDependency("kermit").get()
val VersionCatalog.decompose get() = findDependency("decompose-dep").get()
val VersionCatalog.decomposeComposeExt get() = findDependency("decompose-extensions-compose").get()
val VersionCatalog.jaffree get() = findDependency("jaffree").get()
object Androidx {
const val androidxActivity = "androidx.activity:activity-compose:1.3.1"
const val core = "androidx.core:core-ktx:1.6.0"
const val palette = "androidx.palette:palette-ktx:1.0.0"
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutinesVersion}"
val VersionCatalog.ktlintGradle get() = findDependency("ktlint-gradle").get()
val VersionCatalog.androidGradle get() = findDependency("androidx-gradle-plugin").get()
val VersionCatalog.mosaicGradle get() = findDependency("mosaic-gradle").get()
val VersionCatalog.kotlinComposeGradle get() = findDependency("kotlin-compose-gradle").get()
val VersionCatalog.kotlinGradle get() = findDependency("kotlin-kotlinGradlePlugin").get()
val VersionCatalog.i18n4kGradle get() = findDependency("i18n4k-gradle-plugin").get()
val VersionCatalog.sqlDelightGradle get() = findDependency("sqldelight-gradle-plugin").get()
val VersionCatalog.kotlinSerializationPlugin get() = findDependency("kotlin-serialization").get()
const val junit = "androidx.test.ext:junit:1.1.2"
const val expresso = "androidx.test.espresso:espresso-core:3.3.0"
val VersionCatalog.koinCore get() = findDependency("koin-core").get()
val VersionCatalog.kotlinCoroutines get() = findDependency("kotlin-coroutines").get()
val VersionCatalog.kotlinxSerialization get() = findDependency("kotlinx-serialization-json").get()
val VersionCatalog.ktorClientIOS get() = findDependency("ktor-client-ios").get()
val VersionCatalog.ktorClientAndroid get() = findDependency("ktor-client-android").get()
val VersionCatalog.ktorClientApache get() = findDependency("ktor-client-apache").get()
val VersionCatalog.ktorClientJS get() = findDependency("ktor-client-js").get()
val VersionCatalog.ktorClientCIO get() = findDependency("ktor-client-cio").get()
val VersionCatalog.slf4j get() = findDependency("slf4j-simple").get()
const val gradlePlugin = "com.android.tools.build:gradle:7.0.1"
}
object KTLint {
const val gradlePlugin = "org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}"
}
object JetBrains {
object Kotlin {
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt"
const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlinVersion}"
const val serialization = "org.jetbrains.kotlin:kotlin-serialization:${Versions.kotlinVersion}"
const val testCommon = "org.jetbrains.kotlin:kotlin-test-common:${Versions.kotlinVersion}"
const val testJunit = "org.jetbrains.kotlin:kotlin-test-junit:${Versions.kotlinVersion}"
const val testAnnotationsCommon =
"org.jetbrains.kotlin:kotlin-test-annotations-common:${Versions.kotlinVersion}"
}
object Compose {
// __LATEST_COMPOSE_RELEASE_VERSION__
private const val VERSION = "1.0.0-alpha3"
const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION"
}
}
object Mosaic {
const val gradlePlugin = "com.jakewharton.mosaic:mosaic-gradle-plugin:${Versions.mosaic}"
}
object Decompose {
private const val VERSION = "0.3.1"
const val decompose = "com.arkivanov.decompose:decompose:$VERSION"
const val decomposeIosX64 = "com.arkivanov.decompose:decompose-iosx64:$VERSION"
const val decomposeIosArm64 = "com.arkivanov.decompose:decompose-iosarm64:$VERSION"
const val extensionsCompose = "com.arkivanov.decompose:extensions-compose-jetbrains:$VERSION"
}
object MVIKotlin {
private const val VERSION = "2.0.4"
const val rx = "com.arkivanov.mvikotlin:rx:$VERSION"
const val mvikotlin = "com.arkivanov.mvikotlin:mvikotlin:$VERSION"
const val mvikotlinMain = "com.arkivanov.mvikotlin:mvikotlin-main:$VERSION"
const val coroutines = "com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:$VERSION"
const val keepers = "com.arkivanov.mvikotlin:keepers:$VERSION"
const val mvikotlinMainIosX64 = "com.arkivanov.mvikotlin:mvikotlin-main-iosx64:$VERSION"
const val mvikotlinMainIosArm64 = "com.arkivanov.mvikotlin:mvikotlin-main-iosarm64:$VERSION"
const val mvikotlinLogging = "com.arkivanov.mvikotlin:mvikotlin-logging:$VERSION"
const val mvikotlinTimeTravel = "com.arkivanov.mvikotlin:mvikotlin-timetravel:$VERSION"
const val mvikotlinExtensionsReaktive = "com.arkivanov.mvikotlin:mvikotlin-extensions-reaktive:$VERSION"
}
object Ktor {
val clientCore = "io.ktor:ktor-client-core:${Versions.ktor}"
val clientJson = "io.ktor:ktor-client-json:${Versions.ktor}"
val clientLogging = "io.ktor:ktor-client-logging:${Versions.ktor}"
val clientSerialization = "io.ktor:ktor-client-serialization:${Versions.ktor}"
val auth = "io.ktor:ktor-client-auth:${Versions.ktor}"
val clientAndroid = "io.ktor:ktor-client-android:${Versions.ktor}"
val clientCurl = "io.ktor:ktor-client-curl:${Versions.ktor}"
val clientApache = "io.ktor:ktor-client-apache:${Versions.ktor}"
val slf4j = "org.slf4j:slf4j-simple:${Versions.slf4j}"
val clientIos = "io.ktor:ktor-client-ios:${Versions.ktor}"
val clientCio = "io.ktor:ktor-client-cio:${Versions.ktor}"
val clientJs = "io.ktor:ktor-client-js:${Versions.ktor}"
}
object Internationalization {
const val dep = "de.comahe.i18n4k:i18n4k-core:${Versions.i18n4k}"
const val gradlePlugin = "de.comahe.i18n4k:i18n4k-gradle-plugin:${Versions.i18n4k}"
}
object Extras {
const val youtubeDownloader = "io.github.shabinder:youtube-api-dl:1.3"
const val fuzzyWuzzy = "io.github.shabinder:fuzzywuzzy:1.1"
const val mp3agic = "com.mpatric:mp3agic:0.9.0"
const val jaudioTagger = "com.github.Shabinder:JAudioTagger-Android:1.0"
const val kermit = "co.touchlab:kermit:${Versions.kermit}"
object Android {
// Self Hosted Analytics & Crashlytics (FOSS)
val countly = "ly.count.android:sdk:20.11.8"
val appUpdator = "com.github.amitbd1508:AppUpdater:4.1.0"
}
object Desktop {
val countly = "ly.count.sdk:java:20.11.0"
}
}
object Serialization {
val json = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.kotlinxSerialization}"
}
object SqlDelight {
val runtime = "com.squareup.sqldelight:runtime:${Versions.sqlDelight}"
val coroutineExtensions = "com.squareup.sqldelight:coroutines-extensions:${Versions.sqlDelight}"
const val gradlePlugin = "com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}"
const val androidDriver = "com.squareup.sqldelight:android-driver:${Versions.sqlDelight}"
const val sqliteDriver = "com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}"
const val nativeDriver = "com.squareup.sqldelight:native-driver:${Versions.sqlDelight}"
val nativeDriverMacos = "com.squareup.sqldelight:native-driver-macosx64:${Versions.sqlDelight}"
val jdbcDriver = "org.xerial:sqlite-jdbc:${Versions.sqliteJdbcDriver}"
}
fun DependencyHandler.`implementation`(
dependencyNotation: String,
dependencyConfiguration: ExternalModuleDependency.() -> Unit
): ExternalModuleDependency = addDependencyTo(
this, "implementation", dependencyNotation
) { dependencyConfiguration() }
val VersionCatalog.sqlDelightJDBC get() = findDependency("sqlite-jdbc-driver").get()
val VersionCatalog.sqlDelightNative get() = findDependency("sqldelight-native-driver").get()
val VersionCatalog.sqlDelightAndroid get() = findDependency("sqldelight-android-driver").get()
val VersionCatalog.sqlDelightDriver get() = findDependency("sqldelight-driver").get()

135
buildSrc/deps.versions.toml Normal file
View File

@ -0,0 +1,135 @@
[versions]
kotlin = "1.5.31"
androidCoroutines = "1.5.1"
ktLint = "10.1.0"
mosaic = "0.1.0"
koin = "3.1.2"
kermit = "0.1.9"
mokoParcelize = "0.7.1"
ktor = "1.6.3"
kotlinxSerialization = "1.3.0"
sqlDelight = "1.5.1"
sqliteJdbcDriver = "3.34.0"
slf4j = "1.7.31"
i18n4k = "0.1.3"
essenty = "0.1.3"
multiplatformSettings = "0.7.7"
decompose = "0.3.1"
mviKotlin = "2.0.4"
accompanist = "0.18.0"
statelyVersion = "1.1.10"
statelyIsoVersion = "1.2.0-nmm"
androidxLifecycle = "2.4.0-alpha03"
[libraries]
kotlin-kotlinGradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-serialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
kotlin-kotlinTestCommon = { group = "org.jetbrains.kotlin", name = "kotlin-test-common", version.ref = "kotlin" }
kotlin-kotlinTestJs = { group = "org.jetbrains.kotlin", name = "kotlin-test-js", version.ref = "kotlin" }
kotlin-kotlinTestJunit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit", version.ref = "kotlin" }
kotlin-kotlinTestAnnotationsCommon = { group = "org.jetbrains.kotlin", name = "kotlin-test-annotations-common", version.ref = "kotlin" }
kotlin-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.5.2-native-mt" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
kotlinx-atomicfu = { group = "org.jetbrains.kotlinx", name = "atomicfu", version = "0.16.3" }
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version = "0.2.1" }
kotlin-compose-gradle = { group = "org.jetbrains.compose", name = "compose-gradle-plugin", version = "1.0.0-alpha4-build366" }
mosaic-gradle = { group = "com.jakewharton.mosaic", name = "mosaic-gradle-plugin", version.ref = "mosaic" }
essenty-lifecycle = { group = "com.arkivanov.essenty", name = "lifecycle", version.ref = "essenty" }
essenty-instanceKeeper = { group = "com.arkivanov.essenty", name = "instance-keeper", version.ref = "essenty" }
decompose-dep = { group = "com.arkivanov.decompose", name = "decompose", version.ref = "decompose" }
decompose-extensions-compose = { group = "com.arkivanov.decompose", name = "extensions-compose-jetbrains", version.ref = "decompose" }
mviKotlin-dep = { group = "com.arkivanov.mvikotlin", name = "mvikotlin", version.ref = "mviKotlin" }
mviKotlin-rx = { group = "com.arkivanov.mvikotlin", name = "rx", version.ref = "mviKotlin" }
mviKotlin-main = { group = "com.arkivanov.mvikotlin", name = "mvikotlin-main", version.ref = "mviKotlin" }
mviKotlin-coroutines = { group = "com.arkivanov.mvikotlin", name = "mvikotlin-extensions-coroutines", version.ref = "mviKotlin" }
mviKotlin-keepers = { group = "com.arkivanov.mvikotlin", name = "keepers", version.ref = "mviKotlin" }
mviKotlin-logging = { group = "com.arkivanov.mvikotlin", name = "mvikotlin-logging", version.ref = "mviKotlin" }
mviKotlin-timetravel = { group = "com.arkivanov.mvikotlin", name = "mvikotlin-timetravel", version.ref = "mviKotlin" }
mviKotlin-extensions-reaktive = { group = "com.arkivanov.mvikotlin", name = "mvikotlin-extensions-reaktive", version.ref = "mviKotlin" }
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
ktor-client-json = { group = "io.ktor", name = "ktor-client-json", version.ref = "ktor" }
ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" }
ktor-client-serialization = { group = "io.ktor", name = "ktor-client-serialization", version.ref = "ktor" }
ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" }
ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" }
ktor-client-curl = { group = "io.ktor", name = "ktor-client-curl", version.ref = "ktor" }
ktor-client-apache = { group = "io.ktor", name = "ktor-client-apache", version.ref = "ktor" }
ktor-client-ios = { group = "io.ktor", name = "ktor-client-ios", version.ref = "ktor" }
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
ktor-client-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
i18n4k-core = { group = "de.comahe.i18n4k", name = "i18n4k-core", version.ref = "i18n4k" }
i18n4k-gradle-plugin = { group = "de.comahe.i18n4k", name = "i18n4k-gradle-plugin", version.ref = "i18n4k" }
youtube-downloader = { group = "io.github.shabinder", name = "youtube-api-dl", version = "1.3" }
fuzzy-wuzzy = { group = "io.github.shabinder", name = "fuzzywuzzy", version = "1.1" }
mp3agic = { group = "com.mpatric", name = "mp3agic", version = "0.9.0" }
kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" }
storage-chooser = { group = "com.github.shabinder", name = "storage-chooser", version = "2.0.4.45" }
accompanist-inset = { group = "com.google.accompanist", name = "accompanist-insets", version.ref = "accompanist" }
android-app-notifier = { group = "com.github.amitbd1508", name = "AppUpdater", version = "4.1.0" }
moko-parcelize = { group = "dev.icerock.moko", name = "parcelize", version.ref = "mokoParcelize" }
jaffree = { group = "com.github.kokorin.jaffree", name = "jaffree", version = "2021.08.16" }
multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatformSettings" }
countly-android = { group = "ly.count.android", name = "sdk", version = "20.11.8" }
countly-desktop = { group = "ly.count.sdk", name = "java", version = "20.11.0" }
stately-common = { group = "co.touchlab", name = "stately-common", version.ref = "statelyVersion" }
stately-concurrency = { group = "co.touchlab", name = "stately-concurrency", version.ref = "statelyVersion" }
stately-isolate = { group = "co.touchlab", name = "stately-isolate", version.ref = "statelyIsoVersion" }
stately-iso-collections = { group = "co.touchlab", name = "stately-iso-collections", version.ref = "statelyIsoVersion" }
sqldelight-runtime = { group = "com.squareup.sqldelight", name = "runtime", version.ref = "sqlDelight" }
sqldelight-coroutines-extension = { group = "com.squareup.sqldelight", name = "coroutines-extensions", version.ref = "sqlDelight" }
sqldelight-gradle-plugin = { group = "com.squareup.sqldelight", name = "gradle-plugin", version.ref = "sqlDelight" }
sqldelight-driver = { group = "com.squareup.sqldelight", name = "sqlite-driver", version.ref = "sqlDelight" }
sqldelight-android-driver = { group = "com.squareup.sqldelight", name = "android-driver", version.ref = "sqlDelight" }
sqldelight-native-driver = { group = "com.squareup.sqldelight", name = "native-driver", version.ref = "sqlDelight" }
sqlite-jdbc-driver = { group = "org.xerial", name = "sqlite-jdbc", version.ref = "sqliteJdbcDriver" }
koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" }
koin-test = { group = "io.insert-koin", name = "koin-test", version.ref = "koin" }
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" }
kotlin-js-wrappers-react = { group = "org.jetbrains.kotlin-wrappers", name = "kotlin-react", version = "17.0.2-pre.251-kotlin-1.5.31" }
kotlin-js-wrappers-reactDom = { group = "org.jetbrains.kotlin-wrappers", name = "kotlin-react-dom", version = "17.0.2-pre.251-kotlin-1.5.31" }
kotlin-js-wrappers-styled = { group = "org.jetbrains.kotlin-wrappers", name = "kotlin-styled", version = "5.3.1-pre.250-kotlin-1.5.31" }
kotlin-js-wrappers-ext = { group = "org.jetbrains.kotlin-wrappers", name = "kotlin-extensions", version = "1.0.1-pre.251-kotlin-1.5.31" }
androidx-activity = { group = "androidx.activity", name = "activity-compose", version = "1.3.1" }
androidx-core = { group = "androidx.core", name = "core-ktx", version = "1.6.0" }
androidx-palette = { group = "androidx.palette", name = "palette-ktx", version = "1.0.0" }
androidx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "androidCoroutines" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version = "1.1.2" }
androidx-expresso = { group = "androidx.test.espresso", name = "espresso-core", version = "3.3.0" }
androidx-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version = "4.2.2" }
androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "androidxLifecycle" }
androidx-lifecycle-common = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "androidxLifecycle" }
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
androidx-desugar = { group = "com.android.tools", name = "desugar_jdk_libs", version = "1.1.5" }
leak-canary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version = "2.7" }
junit = { group = "junit", name = "junit", version = "4.13.2" }
ktlint-gradle = { group = "org.jlleitschuh.gradle", name = "ktlint-gradle", version.ref = "ktLint" }
[bundles]
ktor = ["ktor-client-core","ktor-client-json","ktor-client-auth","ktor-client-logging","ktor-client-serialization"]
stately = ["stately-common","stately-concurrency","stately-isolate","stately-iso-collections"]
androidx-lifecycle = ["androidx-lifecycle-service","androidx-lifecycle-common","androidx-lifecycle-runtime"]
androidx-common = ["androidx-activity","androidx-core"]
kotlin-test = ["kotlin-kotlinTestCommon","kotlin-kotlinTestAnnotationsCommon"]
sqldelight = ["sqldelight-runtime","sqldelight-coroutines-extension","sqldelight-driver"]
mviKotlin = ["mviKotlin-dep","mviKotlin-main","mviKotlin-coroutines","mviKotlin-logging","mviKotlin-timetravel"]
kotlinCommon = ["kotlin-coroutines", "kotlin-serialization", "kotlinx-serialization-json", "kotlinx-atomicfu"]
essenty = ["essenty-lifecycle","essenty-instanceKeeper"]
koin-android = ["koin-androidx-compose","koin-android"]
kotlin-js-wrappers = ["kotlin-js-wrappers-react","kotlin-js-wrappers-reactDom","kotlin-js-wrappers-styled","kotlin-js-wrappers-ext"]

View File

@ -0,0 +1,12 @@
enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
@Suppress("UnstableApiUsage")
versionCatalogs {
create("deps") {
from(files("deps.versions.toml"))
}
}
}
rootProject.name = "spotiflyer-build"

View File

@ -6,10 +6,10 @@ kotlin {
sourceSets {
all {
languageSettings.apply {
useExperimentalAnnotation("kotlin.RequiresOptIn")
useExperimentalAnnotation("kotlin.Experimental")
useExperimentalAnnotation("kotlin.time.ExperimentalTime")
useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
optIn("kotlin.RequiresOptIn")
optIn("kotlin.Experimental")
optIn("kotlin.time.ExperimentalTime")
optIn("kotlinx.serialization.ExperimentalSerializationApi")
}
}
}

View File

@ -29,36 +29,23 @@ kotlin {
sourceSets {
all {
languageSettings.apply {
useExperimentalAnnotation("androidx.compose.animation")
optIn("androidx.compose.animation")
}
}
named("commonMain") {
dependencies {
// Decompose
implementation(Decompose.decompose)
// MVI
implementation(MVIKotlin.coroutines)
implementation(MVIKotlin.mvikotlin)
implementation(compose.ui)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.animation)
implementation(Extras.kermit)
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation(JetBrains.Kotlin.coroutines) {
@Suppress("DEPRECATION")
isForce = true
}
implementation(Deps.kotlinCoroutines)
implementation(Deps.decompose)
}
}
named("androidMain") {
dependencies {
implementation(Androidx.androidxActivity)
implementation(Androidx.core)
implementation(Deps.androidXCommonBundle)
}
}
named("desktopMain") {

View File

@ -42,23 +42,24 @@ kotlin {
sourceSets {
named("commonTest") {
dependencies {
implementation(JetBrains.Kotlin.testCommon)
implementation(JetBrains.Kotlin.testAnnotationsCommon)
implementation(Deps.kotlinTestBundle)
}
}
named("androidTest") {
dependencies {
implementation(JetBrains.Kotlin.testJunit)
implementation(Deps.kotlinJunitTest)
}
}
named("desktopTest") {
dependencies {
implementation(JetBrains.Kotlin.testJunit)
implementation(Deps.kotlinJunitTest)
}
}
named("jsTest") {
dependencies {}
dependencies {
implementation(Deps.kotlinJSTest)
}
}
}
}

View File

@ -25,7 +25,7 @@ plugins {
kotlin {
/*IOS Target Can be only built on Mac*/
if(HostOS.isMac){
if (HostOS.isMac) {
val sdkName: String? = System.getenv("SDK_NAME")
val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
if (isiOSDevice) {
@ -50,45 +50,25 @@ kotlin {
sourceSets {
named("commonMain") {
dependencies {
// Decompose
implementation(Decompose.decompose)
// MVI
implementation(MVIKotlin.coroutines)
implementation(MVIKotlin.mvikotlin)
// Koin
implementation(Koin.core)
implementation(Ktor.auth)
implementation(Ktor.clientJson)
implementation(Ktor.clientCore)
implementation(Ktor.clientLogging)
implementation(Ktor.clientSerialization)
// Extras
implementation(Extras.kermit)
implementation(Serialization.json)
implementation("co.touchlab:stately-common:1.1.7")
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation(JetBrains.Kotlin.coroutines) {
@Suppress("DEPRECATION")
isForce = true
}
implementation(Deps.ktorBundle)
implementation(Deps.kotlinxSerialization)
implementation(Deps.kotlinCoroutines)
implementation(Deps.mviKotlinBundle)
implementation(Deps.decompose)
implementation(Deps.koinCore)
}
}
named("androidMain") {
dependencies {
implementation(Androidx.androidxActivity)
implementation(Androidx.core)
implementation(compose.runtime)
implementation(compose.material)
implementation(compose.foundation)
implementation(compose.materialIconsExtended)
implementation(Decompose.extensionsCompose)
implementation(Ktor.clientAndroid)
implementation(Koin.android)
implementation(Deps.androidXCommonBundle)
implementation(Deps.decomposeComposeExt)
implementation(Deps.ktorClientAndroid)
implementation(Deps.koinAndroidBundle)
}
}
@ -99,27 +79,20 @@ kotlin {
implementation(compose.material)
implementation(compose.desktop.common)
implementation(compose.materialIconsExtended)
implementation(Decompose.extensionsCompose)
implementation(Ktor.clientApache)
implementation(Ktor.slf4j)
implementation(Deps.decomposeComposeExt)
implementation(Deps.ktorClientApache)
implementation(Deps.slf4j)
}
}
named("jsMain") {
dependencies {
implementation(Ktor.clientJs)
/*with(KotlinJSWrappers) {
implementation(enforcedPlatform(bom))
implementation(kotlinReact)
implementation(kotlinReactDom)
implementation(kotlinStyled)
}*/
implementation(Deps.ktorClientJS)
}
}
if(HostOS.isMac){
named("iosMain"){
if (HostOS.isMac) {
named("iosMain") {
dependencies {
implementation(Ktor.clientIos)
implementation(Deps.ktorClientIOS)
}
}
}

View File

@ -40,7 +40,7 @@ kotlin {
implementation(project(":common:database"))
implementation(project(":common:data-models"))
implementation(project(":common:dependency-injection"))
implementation(Decompose.extensionsCompose)
implementation(deps.decompose.extensions.compose)
}
}
}

View File

@ -81,6 +81,9 @@ actual fun SpotifyLogo() = getCachedPainter(R.drawable.ic_spotify_logo)
@Composable
actual fun SaavnLogo() = getCachedPainter(R.drawable.ic_jio_saavn_logo)
@Composable
actual fun SoundCloudLogo() = getCachedPainter(R.drawable.ic_soundcloud)
@Composable
actual fun GaanaLogo() = getCachedPainter(R.drawable.ic_gaana)

View File

@ -58,6 +58,9 @@ expect fun SpotifyLogo(): Painter
@Composable
expect fun SaavnLogo(): Painter
@Composable
expect fun SoundCloudLogo(): Painter
@Composable
expect fun YoutubeLogo(): Painter

View File

@ -83,12 +83,14 @@ import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
import com.shabinder.common.models.DownloadRecord
import com.shabinder.common.models.Actions
import com.shabinder.common.models.spotify.Source
import com.shabinder.common.translations.Strings
import com.shabinder.common.uikit.GaanaLogo
import com.shabinder.common.uikit.GithubLogo
import com.shabinder.common.uikit.ImageLoad
import com.shabinder.common.uikit.SaavnLogo
import com.shabinder.common.uikit.ShareImage
import com.shabinder.common.uikit.SoundCloudLogo
import com.shabinder.common.uikit.SpotifyLogo
import com.shabinder.common.uikit.VerticalScrollbar
import com.shabinder.common.uikit.YoutubeLogo
@ -319,6 +321,17 @@ fun AboutColumn(
)
)
}
Spacer(modifier = Modifier.padding(top = 8.dp))
Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) {
Icon(
SoundCloudLogo(),
"${Strings.open()} Sound Cloud",
tint = Color.Unspecified,
modifier = Modifier.clip(SpotiFlyerShapes.medium).clickable(
onClick = { Actions.instance.openPlatform("com.soundcloud.android", "https://soundcloud.com/") }
)
)
}
}
}
Spacer(modifier = Modifier.padding(top = 8.dp))

View File

@ -21,15 +21,19 @@ package com.shabinder.common.uikit
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.res.vectorXmlResource
import androidx.compose.ui.res.useResource
import org.xml.sax.InputSource
@Composable
internal actual fun <T> imageVectorResource(id: T): ImageVector =
vectorXmlResource(id as String)
internal actual fun <T> imageVectorResource(id: T): ImageVector {
val density = LocalDensity.current
return useResource(id as String) {
loadXmlImageVector(InputSource(it), density)
}
}
@Composable
actual fun DownloadImageTick() {
@ -82,6 +86,10 @@ actual fun SpotifyLogo() =
actual fun SaavnLogo() =
getCachedPainter("drawable/ic_jio_saavn_logo.xml")
@Composable
actual fun SoundCloudLogo() =
getCachedPainter("drawable/ic_soundcloud.xml")
@Composable
actual fun YoutubeLogo() =
getCachedPainter("drawable/ic_youtube.xml")

View File

@ -12,7 +12,7 @@ import androidx.compose.ui.unit.dp
actual val MARGIN_SCROLLBAR: Dp = 8.dp
actual typealias ScrollbarAdapter = androidx.compose.foundation.ScrollbarAdapter
actual typealias ScrollbarAdapter = ScrollbarAdapter
@OptIn(ExperimentalFoundationApi::class)
@Composable
@ -23,8 +23,6 @@ actual fun rememberScrollbarAdapter(
): ScrollbarAdapter =
androidx.compose.foundation.rememberScrollbarAdapter(
scrollState = scrollState,
itemCount = itemCount,
averageItemSize = averageItemSize
)
@Composable

View File

@ -10,29 +10,37 @@ kotlin {
dependencies {
implementation(project(":common:data-models"))
implementation(project(":common:database"))
api("org.jetbrains.kotlinx:atomicfu:0.16.2")
api(MultiPlatformSettings.dep)
implementation(MVIKotlin.rx)
with(deps) {
api(multiplatform.settings)
api(kotlinx.atomicfu)
implementation(mviKotlin.rx)
implementation(decompose.dep)
}
}
}
androidMain {
dependencies {
implementation(Extras.mp3agic)
implementation(Extras.Android.countly)
with(deps) {
implementation(mp3agic)
implementation(countly.android)
}
implementation(project(":ffmpeg:android-ffmpeg"))
}
}
desktopMain {
dependencies {
implementation(Extras.mp3agic)
implementation(Extras.Desktop.countly)
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
with(deps) {
implementation(mp3agic)
implementation(countly.desktop)
implementation(jaffree)
}
}
}
jsMain {
dependencies {
implementation(npm("browser-id3-writer", "4.4.0"))
implementation(npm("file-saver", "2.0.4"))
implementation(deps.kotlin.js.wrappers.ext)
}
}
}

View File

@ -117,7 +117,7 @@ class AndroidFileManager(
try {
// Add Mp3 Tags and Add to Library
if(trackDetails.audioFormat != AudioFormat.MP3)
if (trackDetails.audioFormat != AudioFormat.MP3)
throw InvalidDataException("Audio Format is ${trackDetails.audioFormat}, Needs Conversion!")
Mp3File(File(songFile.absolutePath))
@ -166,7 +166,7 @@ class AndroidFileManager(
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture =
withContext(dispatcherIO) {
val cachePath = imageCacheDir() + getNameURL(url)
val cachePath = getImageCachePath(url)
Picture(
image = (loadCachedImage(cachePath, reqWidth, reqHeight) ?: freshImage(
url,
@ -214,7 +214,7 @@ class AndroidFileManager(
// Decode and Cache Full Sized Image in Background
cacheImage(
BitmapFactory.decodeByteArray(input, 0, input.size),
imageCacheDir() + getNameURL(url)
getImageCachePath(url)
)
}
bitmap // return Memory Efficient Bitmap

View File

@ -25,10 +25,14 @@ import com.shabinder.common.models.DownloadResult
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.utils.removeIllegalChars
import com.shabinder.common.utils.requireNotNull
import com.shabinder.database.Database
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.client.HttpClient
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.statement.HttpStatement
import io.ktor.http.contentLength
import io.ktor.http.isSuccess
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
@ -80,12 +84,13 @@ fun FileManager.createDirectories() {
if (!defaultDir().contains("null${fileSeparator()}SpotiFlyer")) {
createDirectory(defaultDir())
createDirectory(imageCacheDir())
createDirectory(defaultDir() + "Tracks/")
createDirectory(defaultDir() + "Albums/")
createDirectory(defaultDir() + "Playlists/")
createDirectory(defaultDir() + "YT_Downloads/")
createDirectory(defaultDir() + "Tracks" + fileSeparator())
createDirectory(defaultDir() + "Albums" + fileSeparator())
createDirectory(defaultDir() + "Playlists" + fileSeparator())
createDirectory(defaultDir() + "YT_Downloads" + fileSeparator())
}
} catch (ignored: Exception) { }
} catch (ignored: Exception) {
}
}
fun FileManager.finalOutputDir(
@ -100,24 +105,50 @@ fun FileManager.finalOutputDir(
removeIllegalChars(subFolder) + this.fileSeparator()
} +
removeIllegalChars(itemName) + extension
/*DIR Specific Operation End*/
fun getNameURL(url: String): String {
return url.substring(url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1, url.length)
.replace('/', '_')
fun FileManager.getImageCachePath(
url: String
): String = imageCacheDir() + getNameFromURL(url, isImage = true)
/*DIR Specific Operation End*/
private fun getNameFromURL(url: String, isImage: Boolean = false): String {
val startIndex = url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1
var fileName = if (startIndex != -1)
url.substring(startIndex).replace('/', '_')
else url.substringAfterLast("/")
// Generify File Extensions
if (isImage) {
if (fileName.length - fileName.lastIndexOf(".") > 5) {
fileName += ".jpeg"
} else {
if (fileName.endsWith(".jpg"))
fileName = fileName.substringBeforeLast(".") + ".jpeg"
}
}
return fileName
}
suspend fun downloadFile(url: String): Flow<DownloadResult> {
suspend fun HttpClient.downloadFile(url: String) = downloadFile(url, this)
suspend fun downloadFile(url: String, client: HttpClient? = null): Flow<DownloadResult> {
return flow {
val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt())
val httpClient = client ?: createHttpClient()
val response = httpClient.get<HttpStatement>(url).execute()
// Not all requests return Content Length
val data = kotlin.runCatching {
ByteArray(response.contentLength().requireNotNull().toInt())
}.getOrNull() ?: byteArrayOf()
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()
val progress = data.size.takeIf { it != 0 }?.let { fileSize ->
(offset * 100f / fileSize).roundToInt()
} ?: 0
emit(DownloadResult.Progress(progress))
} while (currentRead > 0)
if (response.status.isSuccess()) {
@ -125,7 +156,10 @@ suspend fun downloadFile(url: String): Flow<DownloadResult> {
} else {
emit(DownloadResult.Error("File not downloaded"))
}
client.close()
// Close Client if We Created One
if (client == null)
httpClient.close()
}.catch { e ->
e.printStackTrace()
emit(DownloadResult.Error(e.message ?: "File not downloaded"))

View File

@ -8,6 +8,8 @@ import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.features.logging.*
import io.ktor.client.request.*
import io.ktor.client.statement.HttpResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.native.concurrent.SharedImmutable
@ -23,6 +25,18 @@ suspend fun isInternetAccessible(): Boolean {
}
}
// If Fails returns Input Url
suspend inline fun HttpClient.getFinalUrl(
url: String,
crossinline block: HttpRequestBuilder.() -> Unit = {}
): String {
return withContext(dispatcherIO) {
runCatching {
get<HttpResponse>(url,block).call.request.url.toString()
}.getOrNull() ?: url
}
}
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
install(JsonFeature) {

View File

@ -30,17 +30,20 @@ import com.shabinder.common.core_components.removeAllTags
import com.shabinder.common.core_components.setId3v1Tags
import com.shabinder.common.core_components.setId3v2TagsAndSaveFile
import com.shabinder.common.database.SpotiFlyerDatabase
import com.shabinder.common.models.Actions
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.dispatcherIO
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.failure
import com.shabinder.common.models.event.coroutines.map
import com.shabinder.common.models.Actions
import com.shabinder.database.Database
import kotlinx.coroutines.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow
import org.jetbrains.skija.Image
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.skia.Image
import org.koin.dsl.bind
import org.koin.dsl.module
import java.awt.image.BufferedImage
@ -104,9 +107,11 @@ class DesktopFileManager(
override suspend fun cacheImage(image: Any, path: String): Unit = withContext(dispatcherIO) {
try {
val file = File(path)
if(!file.parentFile.exists()) createDirectories()
(image as? BufferedImage)?.let {
ImageIO.write(it, "jpeg", File(path))
}
ImageIO.write(it, "jpeg", file)
}
} catch (e: IOException) {
e.printStackTrace()
}
@ -165,7 +170,7 @@ class DesktopFileManager(
}
SuspendableEvent.success(trackDetails.outputFilePath)
} catch (e: Throwable) {
if(e is JaffreeException) Actions.instance.showPopUpMessage("No FFmpeg found at path.")
if (e is JaffreeException) Actions.instance.showPopUpMessage("No FFmpeg found at path.")
if (songFile.exists()) songFile.delete()
logger.e { "${songFile.absolutePath} could not be created" }
SuspendableEvent.error(e)
@ -175,8 +180,7 @@ class DesktopFileManager(
override fun addToLibrary(path: String) {}
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture {
val cachePath = imageCacheDir() + getNameURL(url)
var picture: ImageBitmap? = loadCachedImage(cachePath, reqWidth, reqHeight)
var picture: ImageBitmap? = loadCachedImage(getImageCachePath(url), reqWidth, reqHeight)
if (picture == null) picture = freshImage(url, reqWidth, reqHeight)
return Picture(image = picture)
}
@ -205,7 +209,7 @@ class DesktopFileManager(
if (result != null) {
GlobalScope.launch(Dispatchers.IO) { // TODO Refactor
cacheImage(result, imageCacheDir() + getNameURL(url))
cacheImage(result, getImageCachePath(url))
}
result.toImageBitmap()
} else null

View File

@ -25,9 +25,6 @@ plugins {
id("de.comahe.i18n4k")
}
val statelyVersion = "1.1.7"
val statelyIsoVersion = "1.1.7-a1"
i18n4k {
inputDirectory = "../../translations"
packageName = "com.shabinder.common.translations"
@ -50,11 +47,13 @@ kotlin {
}
commonMain {
dependencies {
implementation("co.touchlab:stately-concurrency:$statelyVersion")
implementation("co.touchlab:stately-isolate:$statelyIsoVersion")
implementation("co.touchlab:stately-iso-collections:$statelyIsoVersion")
implementation(Extras.youtubeDownloader)
api(Internationalization.dep)
with(deps) {
api(bundles.stately)
api(i18n4k.core)
api(kermit)
api(moko.parcelize)
implementation(youtube.downloader)
}
}
}
}

View File

@ -44,7 +44,7 @@ data class TrackDetails(
var audioQuality: AudioQuality = AudioQuality.KBPS192,
var audioFormat: AudioFormat = AudioFormat.MP4,
var outputFilePath: String, // UriString in Android
var videoID: String? = null,
var videoID: String? = null, // will be used for purposes like Downloadable Link || VideoID etc. based on Provider
) : Parcelable {
val outputMp3Path get() = outputFilePath.substringBeforeLast(".") + ".mp3"
}

View File

@ -12,6 +12,11 @@ sealed class SpotiFlyerException(override val message: String) : Exception(messa
override val message: String = /*${Strings.mp3ConverterBusy()} */"CAUSE:$extraInfo"
) : SpotiFlyerException(message)
data class GeoLocationBlocked(
val extraInfo: String? = null,
override val message: String = "This Content is not Accessible from your Location, try using a VPN! \nCAUSE:$extraInfo"
) : SpotiFlyerException(message)
data class UnknownReason(
val exception: Throwable? = null,
override val message: String = Strings.unknownError()

View File

@ -20,7 +20,7 @@ data class SaavnSong @OptIn(ExperimentalSerializationApi::class) constructor(
// val explicit_content: Int = 0,
val has_lyrics: Boolean = false,
val id: String,
val image: String,
val image: String = "",
val label: String? = null,
val label_url: String? = null,
val language: String,

View File

@ -0,0 +1,13 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Badges(
val pro: Boolean = false,
@SerialName("pro_unlimited")
val proUnlimited: Boolean = false,
val verified: Boolean = false
)

View File

@ -0,0 +1,10 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class CreatorSubscription(
val product: Product = Product()
)

View File

@ -0,0 +1,14 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Format(
@SerialName("mime_type")
val mimeType: String = "",
val protocol: String = ""
) {
val isProgressive get() = protocol == "progressive"
}

View File

@ -0,0 +1,9 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.Serializable
@Serializable
data class Media(
val transcodings: List<Transcoding> = emptyList()
)

View File

@ -0,0 +1,10 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Product(
val id: String = ""
)

View File

@ -0,0 +1,24 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PublisherMetadata(
@SerialName("album_title")
val albumTitle: String = "",
val artist: String = "",
@SerialName("contains_music")
val containsMusic: Boolean = false,
val id: Int = 0,
val isrc: String = "",
val publisher: String = "",
@SerialName("release_title")
val releaseTitle: String = "",
@SerialName("upc_or_ean")
val upcOrEan: String = "",
val urn: String = "",
@SerialName("writer_composer")
val writerComposer: String = ""
)

View File

@ -0,0 +1,22 @@
package com.shabinder.common.models.soundcloud
import com.shabinder.common.models.AudioFormat
import kotlinx.serialization.Serializable
@Serializable
data class Transcoding(
val duration: Int = 0,
val format: Format = Format(),
val preset: String = "",
val quality: String = "", //sq == 128kbps //hq == 256kbps
val snipped: Boolean = false,
val url: String = ""
) {
val audioFormat: AudioFormat = when {
preset.contains("mp3") -> AudioFormat.MP3
preset.contains("aac") || preset.contains("m4a") -> AudioFormat.MP4
preset.contains("flac") -> AudioFormat.FLAC
else -> AudioFormat.UNKNOWN
}
}

View File

@ -0,0 +1,38 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class User(
@SerialName("avatar_url")
val avatarUrl: String = "",
val badges: Badges = Badges(),
val city: String = "",
@SerialName("country_code")
val countryCode: String = "",
@SerialName("first_name")
val firstName: String = "",
@SerialName("followers_count")
val followersCount: Int = 0,
@SerialName("full_name")
val fullName: String = "",
val id: Int = 0,
val kind: String = "",
@SerialName("last_modified")
val lastModified: String = "",
@SerialName("last_name")
val lastName: String = "",
val permalink: String = "",
@SerialName("permalink_url")
val permalinkUrl: String = "",
@SerialName("station_permalink")
val stationPermalink: String = "",
@SerialName("station_urn")
val stationUrn: String = "",
val uri: String = "",
val urn: String = "",
val username: String = "",
val verified: Boolean = false
)

View File

@ -0,0 +1,14 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Visual(
@SerialName("entry_time")
val entryTime: Int = 0,
val urn: String = "",
@SerialName("visual_url")
val visualUrl: String = ""
)

View File

@ -0,0 +1,13 @@
package com.shabinder.common.models.soundcloud
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Visuals(
val enabled: Boolean = false,
//val tracking: Any = Any(),
val urn: String = "",
val visuals: List<Visual> = listOf()
)

View File

@ -0,0 +1,166 @@
package com.shabinder.common.models.soundcloud.resolvemodel
import com.shabinder.common.models.AudioFormat
import com.shabinder.common.models.soundcloud.Media
import com.shabinder.common.models.soundcloud.PublisherMetadata
import com.shabinder.common.models.soundcloud.User
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator
@Serializable
@JsonClassDiscriminator("kind")
sealed class SoundCloudResolveResponseBase {
abstract val kind: String
@SerialName("playlist")
@Serializable
data class SoundCloudResolveResponsePlaylist(
@SerialName("artwork_url")
val artworkUrl: String = "",
@SerialName("calculated_artwork_url")
val calculatedArtworkUrl: String = "", //t500x500, t120x120 // "https://i1.sndcdn.com/artworks-pjsabv9w0EXW3lBJ-nvjDYg-large.jpg" // https://i1.sndcdn.com/artworks-pjsabv9w0EXW3lBJ-nvjDYg-t500x500.jpg
@SerialName("created_at")
val createdAt: String = "",
val description: String = "",
@SerialName("display_date")
val displayDate: String = "",
val duration: Int = 0,
override val kind: String = "",
@SerialName("embeddable_by")
val embeddableBy: String = "",
val genre: String = "",
val id: Int = 0,
@SerialName("is_album")
val isAlbum: Boolean = false,
@SerialName("label_name")
val labelName: String = "",
@SerialName("last_modified")
val lastModified: String = "",
val license: String = "",
@SerialName("likes_count")
val likesCount: Int = 0,
@SerialName("managed_by_feeds")
val managedByFeeds: Boolean = false,
val permalink: String = "",
@SerialName("permalink_url")
val permalinkUrl: String = "",
val `public`: Boolean = false,
@SerialName("published_at")
val publishedAt: String = "",
@SerialName("purchase_title")
val purchaseTitle: String = "",
@SerialName("purchase_url")
val purchaseUrl: String = "",
@SerialName("release_date")
val releaseDate: String = "",
@SerialName("reposts_count")
val repostsCount: Int = 0,
@SerialName("secret_token")
val secretToken: String = "",
@SerialName("set_type")
val setType: String = "",
val sharing: String = "",
@SerialName("tag_list")
val tagList: String = "",
val title: String = "", //"Top 50: Hip-hop & Rap"
@SerialName("track_count")
val trackCount: Int = 0,
var tracks: List<SoundCloudResolveResponseTrack> = emptyList(),
val uri: String = "",
val user: User = User(),
@SerialName("user_id")
val userId: Int = 0
) : SoundCloudResolveResponseBase()
@SerialName("track")
@Serializable
data class SoundCloudResolveResponseTrack(
@SerialName("artwork_url")
val artworkUrl: String = "",
val caption: String = "",
@SerialName("comment_count")
val commentCount: Int = 0,
val commentable: Boolean = false,
@SerialName("created_at")
val createdAt: String = "",
val description: String = "",
@SerialName("display_date")
val displayDate: String = "",
@SerialName("download_count")
val downloadCount: Int = 0,
val downloadable: Boolean = false,
val duration: Int = 0,
@SerialName("embeddable_by")
val embeddableBy: String = "",
@SerialName("full_duration")
val fullDuration: Int = 0,
val genre: String = "",
@SerialName("has_downloads_left")
val hasDownloadsLeft: Boolean = false,
val id: Int = 0,
override val kind: String = "",
@SerialName("label_name")
val labelName: String = "",
@SerialName("last_modified")
val lastModified: String = "",
val license: String = "",
@SerialName("likes_count")
val likesCount: Int = 0,
val media: Media = Media(),
@SerialName("monetization_model")
val monetizationModel: String = "",
val permalink: String = "",
@SerialName("permalink_url")
val permalinkUrl: String = "",
@SerialName("playback_count")
val playbackCount: Int = 0,
val policy: String = "",
val `public`: Boolean = false,
@SerialName("publisher_metadata")
val publisherMetadata: PublisherMetadata = PublisherMetadata(),
@SerialName("purchase_title")
val purchaseTitle: String = "",
@SerialName("purchase_url")
val purchaseUrl: String = "",
@SerialName("release_date")
val releaseDate: String = "",
@SerialName("reposts_count")
val repostsCount: Int = 0,
@SerialName("secret_token")
val secretToken: String = "",
val sharing: String = "",
val state: String = "",
@SerialName("station_permalink")
val stationPermalink: String = "",
@SerialName("station_urn")
val stationUrn: String = "",
val streamable: Boolean = false,
@SerialName("tag_list")
val tagList: String = "",
val title: String = "",
@SerialName("track_authorization")
val trackAuthorization: String = "",
@SerialName("track_format")
val trackFormat: String = "",
val uri: String = "",
val urn: String = "",
val user: User = User(),
@SerialName("user_id")
val userId: Int = 0,
val visuals: String = "",
@SerialName("waveform_url")
val waveformUrl: String = ""
) : SoundCloudResolveResponseBase() {
fun getDownloadableLink(): Pair<String, AudioFormat>? {
return (media.transcodings.firstOrNull {
it.quality == "hq" && (it.format.isProgressive || it.url.contains("progressive"))
} ?: media.transcodings.firstOrNull {
it.quality == "sq" && (it.format.isProgressive || it.url.contains("progressive"))
})?.let {
it.url to it.audioFormat
}
}
}
}

View File

@ -20,5 +20,6 @@ enum class Source {
Spotify,
YouTube,
Gaana,
JioSaavn
JioSaavn,
SoundCloud
}

View File

@ -8,6 +8,7 @@ val globalJson by lazy {
Json {
isLenient = true
ignoreUnknownKeys = true
coerceInputValues = true
}
}

View File

@ -0,0 +1,14 @@
<vector android:height="32dp" android:viewportHeight="1386"
android:viewportWidth="2500" android:width="58dp"
xmlns:aapt="http://schemas.android.com/aapt"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:pathData="M0,1137.74c0,31.02 11.25,54.48 33.74,70.38 22.49,15.9 46.53,21.52 72.13,16.87 24.04,-4.65 40.91,-13.19 50.61,-25.59 9.69,-12.41 14.54,-32.96 14.54,-61.66L171.01,800.37c0,-24.04 -8.34,-44.4 -25.01,-61.08 -16.67,-16.68 -37.03,-25.01 -61.07,-25.01 -23.27,0 -43.24,8.34 -59.91,25.01C8.34,755.97 0,776.33 0,800.37zM267.57,1281.99c0,22.5 7.95,39.36 23.85,50.61 15.9,11.25 36.26,16.87 61.08,16.87 25.59,0 46.34,-5.62 62.24,-16.87 15.9,-11.24 23.85,-28.11 23.85,-50.61L438.58,495.58c0,-23.27 -8.34,-43.24 -25.01,-59.91 -16.67,-16.67 -37.03,-25.01 -61.08,-25.01 -23.27,0 -43.24,8.34 -59.91,25.01 -16.68,16.68 -25.01,36.65 -25.01,59.91zM533.97,1319.22c0,22.49 8.14,39.36 24.43,50.61 16.29,11.24 37.23,16.87 62.82,16.87 24.82,0 45.17,-5.62 61.07,-16.87 15.9,-11.25 23.85,-28.11 23.85,-50.61L706.14,601.44c0,-24.04 -8.34,-44.6 -25.01,-61.66 -16.67,-17.06 -36.64,-25.59 -59.91,-25.59 -24.04,0 -44.6,8.53 -61.66,25.59 -17.06,17.06 -25.59,37.62 -25.59,61.66v717.78zM801.54,1322.71c0,42.66 28.69,63.99 86.09,63.99 57.39,0 86.08,-21.33 86.08,-63.99L973.71,159.38c0,-65.15 -19.78,-101.99 -59.33,-110.52 -25.59,-6.2 -50.8,1.16 -75.62,22.1 -24.82,20.94 -37.23,50.41 -37.23,88.41v1163.33zM1073.76,1356.44L1073.76,90.74c0,-40.33 12.02,-64.37 36.06,-72.13C1161.78,6.2 1213.36,0 1264.54,0c118.66,0 229.18,27.92 331.55,83.76 102.37,55.84 185.16,132.04 248.37,228.59 63.21,96.56 99.85,203 109.94,319.34 47.31,-20.17 97.72,-30.25 151.23,-30.25 108.58,0 201.45,38.39 278.62,115.17 77.17,76.78 115.75,169.07 115.75,276.88 0,108.58 -38.59,201.26 -115.75,278.04 -77.17,76.78 -169.65,115.17 -277.45,115.17l-1012.1,-1.16c-6.98,-2.33 -12.22,-6.59 -15.71,-12.8s-5.23,-11.64 -5.23,-16.29z">
<aapt:attr name="android:fillColor">
<gradient android:endX="1252.675" android:endY="1359.0215"
android:startX="1252.675" android:startY="37.73211" android:type="linear">
<item android:color="#FFFF8800" android:offset="0"/>
<item android:color="#FFFF3300" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -34,31 +34,32 @@ kotlin {
implementation(project(":common:data-models"))
// SQL Delight
implementation(SqlDelight.runtime)
implementation(SqlDelight.coroutineExtensions)
// koin
implementation(Koin.test)
with(deps.sqldelight) {
implementation(runtime)
api(coroutines.extension)
}
}
}
androidMain {
dependencies {
implementation(SqlDelight.androidDriver)
implementation(deps.sqldelight.android.driver)
}
}
desktopMain {
dependencies {
implementation(SqlDelight.sqliteDriver)
implementation(SqlDelight.jdbcDriver)
with(deps) {
implementation(sqldelight.driver)
implementation(sqlite.jdbc.driver)
}
}
}
if (HostOS.isMac) {
val iosMain by getting {
dependencies {
implementation(SqlDelight.nativeDriver)
implementation(deps.sqldelight.native.driver)
}
}
}

View File

@ -14,8 +14,6 @@
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import org.jetbrains.compose.compose
plugins {
id("android-setup")
id("multiplatform-setup")

View File

@ -30,7 +30,6 @@ kotlin {
implementation(project(":common:database"))
implementation(project(":common:providers"))
implementation(project(":common:core-components"))
implementation(SqlDelight.coroutineExtensions)
}
}
}

View File

@ -174,12 +174,10 @@ internal class SpotiFlyerListStoreProvider(dependencies: SpotiFlyerList.Dependen
}
}
private fun List<TrackDetails>.updateTracksStatuses(map: HashMap<String, DownloadStatus>): List<TrackDetails> {
val titleList = this.map { it.title }
val updatedList = mutableListOf<TrackDetails>().also { it.addAll(this) }
for (newTrack in map) {
titleList.indexOf(newTrack.key).let { position ->
private fun List<TrackDetails>.updateTracksStatuses(map: Map<String, DownloadStatus>): List<TrackDetails> {
val updatedList = ArrayList(this)
LinkedHashMap(map).forEach { newTrack ->
indexOfFirst { it.title == newTrack.key }.let { position ->
if (position != -1) {
updatedList.getOrNull(position)?.copy(
downloaded = newTrack.value,

View File

@ -30,7 +30,6 @@ kotlin {
implementation(project(":common:database"))
implementation(project(":common:providers"))
implementation(project(":common:core-components"))
implementation(SqlDelight.coroutineExtensions)
}
}
}

View File

@ -30,7 +30,6 @@ kotlin {
implementation(project(":common:database"))
implementation(project(":common:core-components"))
implementation(project(":common:providers"))
implementation(SqlDelight.coroutineExtensions)
}
}
}

View File

@ -12,23 +12,25 @@ kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":common:data-models"))
implementation(project(":common:database"))
implementation(project(":common:core-components"))
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.2.1")
implementation(Extras.youtubeDownloader)
implementation(Extras.fuzzyWuzzy)
with(deps) {
implementation(project(":common:data-models"))
implementation(project(":common:database"))
implementation(project(":common:core-components"))
implementation(youtube.downloader)
implementation(fuzzy.wuzzy)
implementation(kotlinx.datetime)
}
}
}
androidMain {
dependencies {
implementation(Extras.mp3agic)
implementation(deps.mp3agic)
}
}
desktopMain {
dependencies {
implementation(Extras.mp3agic)
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
implementation(deps.mp3agic)
implementation(deps.jaffree)
}
}
jsMain {

View File

@ -20,7 +20,12 @@ import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.preference_manager.PreferenceManager
import com.shabinder.common.database.DownloadRecordDatabaseQueries
import com.shabinder.common.models.*
import com.shabinder.common.models.AudioFormat
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.dispatcherIO
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.flatMapError
import com.shabinder.common.models.event.coroutines.onSuccess
@ -28,9 +33,9 @@ import com.shabinder.common.models.event.coroutines.success
import com.shabinder.common.models.spotify.Source
import com.shabinder.common.providers.gaana.GaanaProvider
import com.shabinder.common.providers.saavn.SaavnProvider
import com.shabinder.common.providers.sound_cloud.SoundCloudProvider
import com.shabinder.common.providers.spotify.SpotifyProvider
import com.shabinder.common.providers.youtube.YoutubeProvider
import com.shabinder.common.providers.youtube.get
import com.shabinder.common.providers.youtube_music.YoutubeMusic
import com.shabinder.common.providers.youtube_to_mp3.requests.YoutubeMp3
import com.shabinder.common.utils.appendPadded
@ -45,6 +50,7 @@ class FetchPlatformQueryResult(
private val spotifyProvider: SpotifyProvider,
private val youtubeProvider: YoutubeProvider,
private val saavnProvider: SaavnProvider,
private val soundCloudProvider: SoundCloudProvider,
private val youtubeMusic: YoutubeMusic,
private val youtubeMp3: YoutubeMp3,
val fileManager: FileManager,
@ -66,7 +72,7 @@ class FetchPlatformQueryResult(
link.contains("youtube.com", true) || link.contains("youtu.be", true) ->
youtubeProvider.query(link)
// Jio Saavn
// JioSaavn
link.contains("saavn", true) ->
saavnProvider.query(link)
@ -74,6 +80,10 @@ class FetchPlatformQueryResult(
link.contains("gaana", true) ->
gaanaProvider.query(link)
// SoundCloud
link.contains("soundcloud", true) ->
soundCloudProvider.query(link)
else -> {
SuspendableEvent.error(SpotiFlyerException.LinkInvalid(link))
}
@ -122,7 +132,7 @@ class FetchPlatformQueryResult(
ytMp3Link.component2()?.stackTraceToString()
?: "couldn't fetch link for ${track.videoID} ,trying manual extraction"
)
appendLine("Trying Local Extraction")
//appendLine("Trying Local Extraction")
null
} else {
audioFormat = AudioFormat.MP3
@ -130,6 +140,20 @@ class FetchPlatformQueryResult(
}
}
}
Source.SoundCloud -> {
audioFormat = track.audioFormat
soundCloudProvider.getDownloadURL(track).let {
if (it is SuspendableEvent.Failure || it.component1().isNullOrEmpty()) {
appendPadded(
"SoundCloud Provider Failed for ${track.title}:",
it.component2()?.stackTraceToString()
?: "couldn't fetch link for ${track.trackUrl}"
)
null
} else
it.component1()
}
}
else -> {
appendPadded(
"Invalid Arguments",

View File

@ -2,6 +2,7 @@ package com.shabinder.common.providers
import com.shabinder.common.providers.gaana.GaanaProvider
import com.shabinder.common.providers.saavn.SaavnProvider
import com.shabinder.common.providers.sound_cloud.SoundCloudProvider
import com.shabinder.common.providers.spotify.SpotifyProvider
import com.shabinder.common.providers.spotify.token_store.TokenStore
import com.shabinder.common.providers.youtube.YoutubeProvider
@ -16,7 +17,8 @@ fun providersModule(enableNetworkLogs: Boolean) = module {
single { GaanaProvider(get(), get(), get()) }
single { SaavnProvider(get(), get(), get()) }
single { YoutubeProvider(get(), get(), get()) }
single { SoundCloudProvider(get(), get(), get()) }
single { YoutubeMp3(get(), get()) }
single { YoutubeMusic(get(), get(), get(), get(), get()) }
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
}

View File

@ -19,6 +19,7 @@ package com.shabinder.common.providers.gaana
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.core_components.file_manager.getImageCachePath
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.SpotiFlyerException
@ -124,8 +125,7 @@ class GaanaProvider(
title = it.track_title,
artists = it.artist.map { artist -> artist?.name.toString() },
durationSec = it.duration,
albumArtPath = fileManager.imageCacheDir() + (it.artworkLink.substringBeforeLast('/')
.substringAfterLast('/')) + ".jpeg",
albumArtPath = fileManager.getImageCachePath(it.artworkLink),
albumName = it.album_title,
genre = it.genre?.mapNotNull { genre -> genre?.name } ?: emptyList(),
year = it.release_date,

View File

@ -25,11 +25,13 @@ import com.shabinder.common.models.gaana.GaanaSong
import io.ktor.client.*
import io.ktor.client.request.*
private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990"
private val BASE_URL get() = "${corsApi}https://api.gaana.com"
interface GaanaRequests {
companion object {
private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990"
private val BASE_URL get() = "${corsApi}https://api.gaana.com"
}
val httpClient: HttpClient
/*

View File

@ -3,6 +3,7 @@ package com.shabinder.common.providers.saavn
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.core_components.file_manager.getImageCachePath
import com.shabinder.common.models.*
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.saavn.SaavnSong
@ -28,7 +29,7 @@ class SaavnProvider(
).apply {
val pageLink = fullLink.substringAfter("saavn.com/").substringBefore("?")
when {
pageLink.contains("/song/", true) -> {
pageLink.contains("song/", true) -> {
getSong(fullLink).value.let {
folderType = "Tracks"
subFolder = ""
@ -37,7 +38,7 @@ class SaavnProvider(
coverUrl = it.image.replace("http:", "https:")
}
}
pageLink.contains("/album/", true) -> {
pageLink.contains("album/", true) -> {
getAlbum(fullLink).value.let {
folderType = "Albums"
subFolder = removeIllegalChars(it.title)
@ -46,7 +47,7 @@ class SaavnProvider(
coverUrl = it.image.replace("http:", "https:")
}
}
pageLink.contains("/featured/", true) -> { // Playlist
pageLink.contains("featured/", true) -> { // Playlist
getPlaylist(fullLink).value.let {
folderType = "Playlists"
subFolder = removeIllegalChars(it.listname)
@ -68,7 +69,7 @@ class SaavnProvider(
artists = it.artistMap.keys.toMutableSet().apply { addAll(it.singers.split(",")) }.toList(),
durationSec = it.duration.toInt(),
albumName = it.album,
albumArtPath = fileManager.imageCacheDir() + (it.image.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg",
albumArtPath = fileManager.getImageCachePath(it.image),
year = it.year,
comment = it.copyright_text,
trackUrl = it.perma_url,

View File

@ -18,9 +18,16 @@ import io.github.shabinder.utils.getBoolean
import io.github.shabinder.utils.getJsonArray
import io.github.shabinder.utils.getJsonObject
import io.github.shabinder.utils.getString
import io.ktor.client.*
import io.ktor.client.request.*
import kotlinx.serialization.json.*
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlin.collections.set
interface JioSaavnRequests {
@ -32,9 +39,9 @@ interface JioSaavnRequests {
trackName: String,
trackArtists: List<String>,
preferredQuality: AudioQuality
): SuspendableEvent<Pair<String,AudioQuality>, Throwable> = searchForSong(trackName).map { songs ->
val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull() ?:
throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName")
): SuspendableEvent<Pair<String, AudioQuality>, Throwable> = searchForSong(trackName).map { songs ->
val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull()
?: throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName")
var audioQuality: AudioQuality = AudioQuality.KBPS160
val m4aLink: String by getSongFromID(bestMatch).map { song ->
@ -46,7 +53,7 @@ interface JioSaavnRequests {
song.media_url.requireNotNull().replaceAfterLast("_", "${optimalQuality.kbps}.mp4")
}
Pair(m4aLink,audioQuality)
Pair(m4aLink, audioQuality)
}
suspend fun searchForSong(
@ -235,8 +242,8 @@ interface JioSaavnRequests {
for (result in tracks) {
var hasCommonWord = false
val resultName = result.title.lowercase().replace("/", " ")
val trackNameWords = trackName.lowercase().split(" ")
val resultName = result.title.toLowerCase().replace("/", " ")
val trackNameWords = trackName.toLowerCase().split(" ")
for (nameWord in trackNameWords) {
if (nameWord.isNotBlank() && FuzzySearch.partialRatio(nameWord, resultName) > 85) hasCommonWord = true
@ -256,11 +263,11 @@ interface JioSaavnRequests {
// String Containing All Artist Names from JioSaavn Search Result
val artistListString = mutableSetOf<String>().apply {
result.more_info?.singers?.split(",")?.let { addAll(it) }
result.more_info?.primary_artists?.lowercase()?.split(",")?.let { addAll(it) }
result.more_info?.primary_artists?.toLowerCase()?.split(",")?.let { addAll(it) }
}.joinToString(" , ")
for (artist in trackArtists) {
if (FuzzySearch.partialRatio(artist.lowercase(), artistListString) > 85)
if (FuzzySearch.partialRatio(artist.toLowerCase(), artistListString) > 85)
artistMatchNumber++
}

View File

@ -0,0 +1,115 @@
package com.shabinder.common.providers.sound_cloud
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.core_components.file_manager.getImageCachePath
import com.shabinder.common.models.AudioFormat
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponsePlaylist
import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponseTrack
import com.shabinder.common.models.spotify.Source
import com.shabinder.common.providers.sound_cloud.requests.SoundCloudRequests
import com.shabinder.common.providers.sound_cloud.requests.doAuthenticatedRequest
import com.shabinder.common.utils.requireNotNull
import io.github.shabinder.utils.getString
import io.ktor.client.HttpClient
import kotlinx.serialization.json.JsonObject
class SoundCloudProvider(
private val logger: Kermit,
private val fileManager: FileManager,
override val httpClient: HttpClient,
) : SoundCloudRequests {
suspend fun query(fullURL: String) = SuspendableEvent {
PlatformQueryResult(
folderType = "",
subFolder = "",
title = "",
coverUrl = "",
trackList = listOf(),
Source.SoundCloud
).apply {
when (val response = fetchResult(fullURL)) {
is SoundCloudResolveResponseTrack -> {
folderType = "Tracks"
subFolder = ""
trackList = listOf(response).toTrackDetailsList(folderType, subFolder)
coverUrl = response.artworkUrl
title = response.title
}
is SoundCloudResolveResponsePlaylist -> {
folderType = "Playlists"
subFolder = response.title
trackList = response.tracks.toTrackDetailsList(folderType, subFolder)
coverUrl = response.artworkUrl.ifBlank { response.calculatedArtworkUrl }
title = response.title
}
}
}
}
suspend fun getDownloadURL(trackDetails: TrackDetails) = SuspendableEvent {
doAuthenticatedRequest<JsonObject>(trackDetails.videoID.requireNotNull()).getString("url")
}
private fun List<SoundCloudResolveResponseTrack>.toTrackDetailsList(
type: String,
subFolder: String
): List<TrackDetails> =
map {
val downloadableInfo = it.getDownloadableLink()
TrackDetails(
title = it.title,
//trackNumber = it.track_number,
genre = listOf(it.genre),
artists = /*it.artists?.map { artist -> artist?.name.toString() } ?:*/ listOf(it.user.username.ifBlank { it.genre }),
albumArtists = /*it.album?.artists?.mapNotNull { artist -> artist?.name } ?:*/ emptyList(),
durationSec = (it.duration / 1000),
albumArtPath = fileManager.getImageCachePath(it.artworkUrl.formatArtworkUrl()),
albumName = "", //it.album?.name,
year = runCatching { it.displayDate.substring(0, 4) }.getOrNull(),
comment = it.caption,
trackUrl = it.permalinkUrl,
downloaded = it.updateStatusIfPresent(type, subFolder),
source = Source.SoundCloud,
albumArtURL = it.artworkUrl.formatArtworkUrl(),
outputFilePath = fileManager.finalOutputDir(
it.title,
type,
subFolder,
fileManager.defaultDir()/*,".m4a"*/
),
audioQuality = AudioQuality.KBPS128,
videoID = downloadableInfo?.first,
audioFormat = downloadableInfo?.second ?: AudioFormat.MP3
)
}
private fun SoundCloudResolveResponseTrack.updateStatusIfPresent(
folderType: String,
subFolder: String
): DownloadStatus {
return if (fileManager.isPresent(
fileManager.finalOutputDir(
title,
folderType,
subFolder,
fileManager.defaultDir()
)
)
) { // Download Already Present!!
DownloadStatus.Downloaded
} else
DownloadStatus.NotDownloaded
}
private fun String.formatArtworkUrl(): String {
return substringBeforeLast("-") + "-t500x500." + substringAfterLast(".")
}
}

View File

@ -0,0 +1,138 @@
package com.shabinder.common.providers.sound_cloud.requests
import com.shabinder.common.core_components.utils.getFinalUrl
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase
import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponsePlaylist
import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponseTrack
import io.ktor.client.HttpClient
import io.ktor.client.features.ClientRequestException
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import kotlinx.serialization.InternalSerializationApi
interface SoundCloudRequests {
val httpClient: HttpClient
suspend fun fetchResult(url: String): SoundCloudResolveResponseBase {
@Suppress("NAME_SHADOWING")
var url = url
// Fetch Full URL if Input is Shortened URL from App
if (url.contains("soundcloud.app"))
url = httpClient.getFinalUrl(url)
return getResponseObj(url).run {
when (this) {
is SoundCloudResolveResponseTrack -> {
getTrack()
}
is SoundCloudResolveResponsePlaylist -> {
populatePlaylist()
}
else -> throw SpotiFlyerException.FeatureNotImplementedYet()
}
}
}
@Suppress("NAME_SHADOWING")
suspend fun SoundCloudResolveResponseTrack.getTrack() = apply {
val track = populateTrackInfo()
if (track.policy == "BLOCK")
throw SpotiFlyerException.GeoLocationBlocked(extraInfo = "Use VPN to access ${track.title}")
if (!track.streamable)
throw SpotiFlyerException.LinkInvalid("\nSound Cloud Reports that ${track.title} is not streamable !\n")
return track
}
@Suppress("NAME_SHADOWING")
suspend fun SoundCloudResolveResponsePlaylist.populatePlaylist(): SoundCloudResolveResponsePlaylist = apply {
supervisorScope {
try {
tracks = tracks.map {
async {
runCatching {
it.populateTrackInfo()
}.getOrNull() ?: it
}
}.awaitAll()
} catch (e: Throwable) {
e.printStackTrace()
}
}
}
private suspend fun SoundCloudResolveResponseTrack.populateTrackInfo(): SoundCloudResolveResponseTrack {
if (media.transcodings.isNotEmpty())
return this
val infoURL = URLS.TRACK_INFO.buildURL(id.toString())
return httpClient.get(infoURL) {
parameter("client_id", CLIENT_ID)
}
}
private suspend fun getResponseObj(url: String, clientID: String = CLIENT_ID): SoundCloudResolveResponseBase {
val itemURL = URLS.RESOLVE.buildURL(url)
val resp: SoundCloudResolveResponseBase = try {
httpClient.get(itemURL) {
parameter("client_id", clientID)
}
} catch (e: ClientRequestException) {
if (clientID != ALT_CLIENT_ID)
return getResponseObj(url, ALT_CLIENT_ID)
throw e
}
val tracksPresent = (resp is SoundCloudResolveResponsePlaylist && resp.tracks.isNotEmpty())
if (!tracksPresent && clientID != ALT_CLIENT_ID)
return getResponseObj(ALT_CLIENT_ID)
return resp
}
@Suppress("unused")
companion object {
private enum class URLS(val buildURL: (arg: String) -> String) {
RESOLVE({ "https://api-v2.soundcloud.com/resolve?url=$it}" }),
PLAYLIST_LIKED({ "https://api-v2.soundcloud.com/users/$it/playlists/liked_and_owned?limit=200" }),
FAVORITES({ "'https://api-v2.soundcloud.com/users/$it/track_likes?limit=200" }),
COMMENTED({ "https://api-v2.soundcloud.com/users/$it/comments" }),
TRACKS({ "https://api-v2.soundcloud.com/users/$it/tracks?limit=200" }),
ALL({ "https://api-v2.soundcloud.com/profile/soundcloud:users:$it?limit=200" }),
TRACK_INFO({ "https://api-v2.soundcloud.com/tracks/$it" }),
ORIGINAL_DOWNLOAD({ "https://api-v2.soundcloud.com/tracks/$it/download" }),
USER({ "https://api-v2.soundcloud.com/users/$it" }),
ME({ "https://api-v2.soundcloud.com/me?oauth_token=$it" }),
}
const val CLIENT_ID = "a3e059563d7fd3372b49b37f00a00bcf"
const val ALT_CLIENT_ID = "2t9loNQH90kzJcsFCODdigxfp325aq4z"
}
}
@OptIn(InternalSerializationApi::class)
suspend inline fun <reified T : Any> SoundCloudRequests.doAuthenticatedRequest(url: String): T {
var clientID: String = SoundCloudRequests.CLIENT_ID
return try {
httpClient.get(url) {
parameter("client_id", clientID)
}
} catch (e: ClientRequestException) {
if (clientID != SoundCloudRequests.ALT_CLIENT_ID) {
clientID = SoundCloudRequests.ALT_CLIENT_ID
return httpClient.get(url) {
parameter("client_id", clientID)
}
}
throw e
}
}

View File

@ -19,6 +19,7 @@ package com.shabinder.common.providers.spotify
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.core_components.file_manager.getImageCachePath
import com.shabinder.common.core_components.utils.createHttpClient
import com.shabinder.common.models.*
import com.shabinder.common.models.event.coroutines.SuspendableEvent
@ -79,7 +80,7 @@ class SpotifyProvider(
if (type == "episode" || type == "show") {
throw SpotiFlyerException.FeatureNotImplementedYet(
"Support for Spotify's ${type.uppercase()} isn't implemented yet"
"Support for Spotify's ${type.toUpperCase()} isn't implemented yet"
)
}
@ -201,9 +202,7 @@ class SpotifyProvider(
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
albumArtists = it.album?.artists?.mapNotNull { artist -> artist?.name } ?: emptyList(),
durationSec = (it.duration_ms / 1000).toInt(),
albumArtPath = fileManager.imageCacheDir() + (it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast(
'/'
) + ".jpeg",
albumArtPath = fileManager.getImageCachePath(it.album?.images?.firstOrNull()?.url ?: ""),
albumName = it.album?.name,
year = it.album?.release_date,
comment = "Genres:${it.album?.genres?.joinToString()}",

View File

@ -19,6 +19,7 @@ package com.shabinder.common.providers.youtube
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.core_components.file_manager.getImageCachePath
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.SpotiFlyerException
@ -30,7 +31,7 @@ import io.github.shabinder.YoutubeDownloader
import io.github.shabinder.models.YoutubeVideo
import io.github.shabinder.models.formats.Format
import io.github.shabinder.models.quality.AudioQuality
import io.ktor.client.*
import io.ktor.client.HttpClient
class YoutubeProvider(
private val httpClient: HttpClient,
@ -108,13 +109,14 @@ class YoutubeProvider(
title = name
trackList = videos.map {
val imageURL = "https://i.ytimg.com/vi/${it.videoId}/hqdefault.jpg"
TrackDetails(
title = it.title ?: "N/A",
artists = listOf(it.author ?: "N/A"),
durationSec = it.lengthSeconds,
albumArtPath = fileManager.imageCacheDir() + it.videoId + ".jpeg",
albumArtPath = fileManager.getImageCachePath(imageURL),
source = Source.YouTube,
albumArtURL = "https://i.ytimg.com/vi/${it.videoId}/hqdefault.jpg",
albumArtURL = imageURL,
downloaded = if (fileManager.isPresent(
fileManager.finalOutputDir(
itemName = it.title ?: "N/A",
@ -155,7 +157,7 @@ class YoutubeProvider(
val video = ytDownloader.getVideo(searchId)
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
val detail = video.videoDetails
val name = detail.title?.replace(detail.author?.uppercase() ?: "", "", true)
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
?: detail.title ?: ""
// logger.i{ detail.toString() }
trackList = listOf(
@ -163,9 +165,9 @@ class YoutubeProvider(
title = name,
artists = listOf(detail.author ?: "N/A"),
durationSec = detail.lengthSeconds,
albumArtPath = fileManager.imageCacheDir() + "$searchId.jpeg",
albumArtPath = fileManager.getImageCachePath(coverUrl),
source = Source.YouTube,
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
albumArtURL = coverUrl,
downloaded = if (fileManager.isPresent(
fileManager.finalOutputDir(
itemName = name,
@ -179,7 +181,12 @@ class YoutubeProvider(
else {
DownloadStatus.NotDownloaded
},
outputFilePath = fileManager.finalOutputDir(name, folderType, subFolder, fileManager.defaultDir()/*,".m4a"*/),
outputFilePath = fileManager.finalOutputDir(
name,
folderType,
subFolder,
fileManager.defaultDir()/*,".m4a"*/
),
videoID = searchId
)
)

View File

@ -18,19 +18,33 @@ package com.shabinder.common.providers.youtube_music
import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.models.*
import com.shabinder.common.models.AudioFormat
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.YoutubeTrack
import com.shabinder.common.models.corsApi
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.flatMap
import com.shabinder.common.models.event.coroutines.flatMapError
import com.shabinder.common.models.event.coroutines.map
import com.shabinder.common.providers.youtube.YoutubeProvider
import com.shabinder.common.providers.youtube.get
import com.shabinder.common.providers.youtube_to_mp3.requests.YoutubeMp3
import io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.serialization.json.*
import io.ktor.client.HttpClient
import io.ktor.client.request.headers
import io.ktor.client.request.post
import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
import kotlin.collections.set
import kotlin.math.absoluteValue
@ -50,7 +64,7 @@ class YoutubeMusic constructor(
suspend fun findMp3SongDownloadURLYT(
trackDetails: TrackDetails,
preferredQuality: AudioQuality = fileManager.preferenceManager.audioQuality
): SuspendableEvent<Pair<String,AudioQuality>, Throwable> {
): SuspendableEvent<Pair<String, AudioQuality>, Throwable> {
return getYTIDBestMatch(trackDetails).flatMap { videoID ->
// As YT compress Audio hence there is no benefit of quality for more than 192
val optimalQuality =
@ -69,7 +83,7 @@ class YoutubeMusic constructor(
}
}*/.map {
trackDetails.audioFormat = AudioFormat.MP3
Pair(it,optimalQuality)
Pair(it, optimalQuality)
}
}
}
@ -168,7 +182,7 @@ class YoutubeMusic constructor(
! 4 - Duration (hh:mm:ss)
!
! We blindly gather all the details we get our hands on, then
! cherry pick the details we need based on their index numbers,
! cherry-pick the details we need based on their index numbers,
! we do so only if their Type is 'Song' or 'Video
*/
@ -180,7 +194,7 @@ class YoutubeMusic constructor(
/*
Filter Out dummies here itself
! 'musicResponsiveListItemFlexColumnRenderer' should have more that one
! sub-block, if not its a dummy, why does the YTM response contain dummies?
! sub-block, if not it is a dummy, why does the YTM response contain dummies?
! I have no clue. We skip these.
! Remember that we appended the linkBlock to result, treating that like the
@ -189,7 +203,7 @@ class YoutubeMusic constructor(
*/
for (detailArray in result.subList(0, result.size - 1)) {
for (detail in detailArray.jsonArray) {
if (detail.jsonObject["musicResponsiveListItemFlexColumnRenderer"]?.jsonObject?.size ?: 0 < 2) continue
if ((detail.jsonObject["musicResponsiveListItemFlexColumnRenderer"]?.jsonObject?.size ?: 0) < 2) continue
// if not a dummy, collect All Variables
val details =
@ -262,8 +276,8 @@ class YoutubeMusic constructor(
// most song results on youtube go by $artist - $songName or artist1/artist2
var hasCommonWord = false
val resultName = result.name?.lowercase()?.replace("-", " ")?.replace("/", " ") ?: ""
val trackNameWords = trackName.lowercase().split(" ")
val resultName = result.name?.toLowerCase()?.replace("-", " ")?.replace("/", " ") ?: ""
val trackNameWords = trackName.toLowerCase().split(" ")
for (nameWord in trackNameWords) {
if (nameWord.isNotBlank() && FuzzySearch.partialRatio(
@ -287,8 +301,8 @@ class YoutubeMusic constructor(
if (result.type == "Song") {
for (artist in trackArtists) {
if (FuzzySearch.ratio(
artist.lowercase(),
result.artist?.lowercase() ?: ""
artist.toLowerCase(),
result.artist?.toLowerCase() ?: ""
) > 85
)
artistMatchNumber++
@ -296,8 +310,8 @@ class YoutubeMusic constructor(
} else { // i.e. is a Video
for (artist in trackArtists) {
if (FuzzySearch.partialRatio(
artist.lowercase(),
result.name?.lowercase() ?: ""
artist.toLowerCase(),
result.name?.toLowerCase() ?: ""
) > 85
)
artistMatchNumber++

View File

@ -1,10 +1,15 @@
package com.shabinder.common.providers
import com.shabinder.common.core_components.utils.createHttpClient
import com.shabinder.common.core_components.utils.getFinalUrl
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.providers.utils.CommonUtils
import com.shabinder.common.providers.utils.SpotifyUtils
import com.shabinder.common.providers.utils.SpotifyUtils.toTrackDetailsList
import io.github.shabinder.runBlocking
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.InternalSerializationApi
import kotlin.test.Test
class TestSpotifyTrackMatching {
@ -17,7 +22,15 @@ class TestSpotifyTrackMatching {
private val spotifyToken: String?
get() = null
// get() = "BQB41HqrLcrh5eRYaL97GvaH6tRe-1EktQ8VGTWUQuFnYVWBEoTcF7T_8ogqVn1GHl9HCcMiQ0HBT-ybC74"
// get() = "BQB41HqrLcrh5eRYaL97GvaH6tRe-1EktQ8VGTWUQuFnYVWBEoTcF7T_8ogqVn1GHl9HCcMiQ0HBT-ybC74"
@OptIn(InternalSerializationApi::class)
@Test
fun testRandomThing() = runBlocking {
val res = createHttpClient().getFinalUrl("https://soundcloud.app.goo.gl/vrBzR")
println(res)
}
@Test
fun matchVideo() = runBlocking {

View File

@ -33,9 +33,10 @@ fun org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.generateFramewor
export(project(":common:providers"))
export(project(":common:list"))
export(project(":common:preference"))
export(Decompose.decompose)
export(MVIKotlin.mvikotlinMain)
export(MVIKotlin.mvikotlinLogging)
with(deps) {
export(decompose.dep)
export(bundles.mviKotlin)
}
}
}
@ -71,7 +72,6 @@ kotlin {
implementation(project(":common:providers"))
implementation(project(":common:core-components"))
implementation(project(":common:preference"))
implementation(SqlDelight.coroutineExtensions)
}
}
}
@ -86,9 +86,10 @@ kotlin {
api(project(":common:list"))
api(project(":common:main"))
api(project(":common:preference"))
api(Decompose.decompose)
api(MVIKotlin.mvikotlinMain)
api(MVIKotlin.mvikotlinLogging)
with(deps) {
api(decompose.dep)
api(bundles.mviKotlin)
}
}
}
}
@ -100,8 +101,11 @@ val packForXcode by tasks.creating(Sync::class) {
group = "build"
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
val targetName = "ios"
val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(targetName)
.binaries.getFramework(mode)
val framework =
kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(
targetName
)
.binaries.getFramework(mode)
inputs.property("mode", mode)
dependsOn(framework.linkTask)
val targetDir = File(buildDir, "xcode-frameworks")

View File

@ -19,38 +19,39 @@ application {
}
dependencies {
implementation(Koin.core)
implementation(project(":common:database"))
implementation(project(":common:data-models"))
implementation(project(":common:dependency-injection"))
implementation(project(":common:root"))
implementation(project(":common:main"))
implementation(project(":common:list"))
implementation(project(":common:list"))
with(deps) {
implementation(Koin.core)
implementation(project(":common:database"))
implementation(project(":common:data-models"))
implementation(project(":common:dependency-injection"))
implementation(project(":common:root"))
implementation(project(":common:main"))
implementation(project(":common:list"))
implementation(project(":common:list"))
// Decompose
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose)
// Decompose
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose)
// MVI
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.mvikotlinMain)
// MVI
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.mvikotlinMain)
// Koin
implementation(Koin.core)
// Koin
implementation(Koin.core)
// Matomo
implementation("org.piwik.java.tracking:matomo-java-tracker:1.6")
// Matomo
implementation(Ktor.slf4j)
implementation(Ktor.clientCore)
implementation(Ktor.clientJson)
implementation(Ktor.clientApache)
implementation(Ktor.clientLogging)
implementation(Ktor.clientSerialization)
implementation(Serialization.json)
// testDeps
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
implementation(Ktor.slf4j)
implementation(Ktor.clientCore)
implementation(Ktor.clientJson)
implementation(Ktor.clientApache)
implementation(Ktor.clientLogging)
implementation(Ktor.clientSerialization)
implementation(Serialization.json)
// testDeps
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {

View File

@ -32,9 +32,12 @@ kotlin {
kotlinOptions.jvmTarget = "1.8"
}
}
tasks.named<Copy>("jvmProcessResources") {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
sourceSets {
val jvmMain by getting {
resources.srcDirs("../common/data-models/src/main/res")
dependencies {
implementation(compose.desktop.currentOs)
implementation(project(":common:database"))
@ -44,19 +47,21 @@ kotlin {
implementation(project(":common:compose"))
implementation(project(":common:providers"))
implementation(project(":common:root"))
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
// Decompose
implementation(Decompose.decompose)
implementation(Decompose.extensionsCompose)
with(deps) {
implementation(jaffree)
// MVI
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.mvikotlinMain)
// Koin
implementation(Koin.core)
with(decompose) {
implementation(dep)
implementation(extensions.compose)
}
with(mviKotlin) {
implementation(dep)
implementation(main)
}
implementation(koin.core)
}
}
}
val jvmTest by getting

View File

@ -1,26 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp"
android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#516AEC" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
<path android:fillColor="#EC7EBA" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
<path android:fillColor="#EC7EBA" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>
<path android:fillColor="#6A82FB" android:pathData="m496,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#6A82FB" android:pathData="m40,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#FC5C7D" android:pathData="m416,512h-320c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h320c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#4AFC5C7D" android:pathData="m256,443.552c-11.78,0 -23.56,-4.484 -32.527,-13.452l-134.786,-134.787c-10.503,-10.502 -16.287,-24.463 -16.287,-39.313 0,-14.708 5.688,-28.573 16.017,-39.042 10.31,-10.451 24.214,-16.233 39.151,-16.284h0.189c14.966,0 29.552,6.009 40.05,16.507l32.193,32.192v-193.373c0,-30.878 25.122,-56 56,-56s56,25.122 56,56v193.373l32.687,-32.687c10.501,-10.502 24.463,-16.286 39.313,-16.286 14.708,0 28.573,5.688 39.042,16.017 10.45,10.31 16.233,24.214 16.284,39.151 0.051,15.032 -5.966,29.698 -16.507,40.24l-134.292,134.292c-8.967,8.968 -20.747,13.452 -32.527,13.452zM127.761,232.673c-0.028,0 -0.056,0 -0.084,0 -6.349,0.021 -12.202,2.421 -16.479,6.758 -4.383,4.443 -6.797,10.327 -6.797,16.569 0,6.302 2.456,12.228 6.914,16.686l134.785,134.787c5.459,5.459 14.341,5.459 19.8,0l134.292,-134.292c4.556,-4.557 7.157,-10.937 7.134,-17.504 -0.021,-6.349 -2.421,-12.202 -6.758,-16.479 -4.443,-4.383 -10.327,-6.797 -16.569,-6.797 -6.302,0 -12.228,2.456 -16.687,6.914l-60,60c-4.575,4.577 -11.456,5.945 -17.437,3.469 -5.977,-2.478 -9.875,-8.313 -9.875,-14.784v-232c0,-13.234 -10.767,-24 -24,-24 -13.234,0 -24,10.766 -24,24v232c0,6.471 -3.898,12.306 -9.877,14.782 -5.978,2.476 -12.861,1.108 -17.437,-3.469l-59.506,-59.505c-4.536,-4.537 -10.882,-7.135 -17.419,-7.135z"/>
</vector>

View File

@ -1,26 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,4c4.41,0 8,3.59 8,8s-3.59,8 -8,8s-8,-3.59 -8,-8S7.59,4 12,4M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2L12,2zM13,12l0,-4h-2l0,4H8l4,4l4,-4H13z"/>
</vector>

View File

@ -1,31 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="38dp" android:height="38dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="m512,256c0,141.387 -114.613,256 -256,256s-256,-114.613 -256,-256 114.613,-256 256,-256 256,114.613 256,256zM512,256">
<aapt:attr name="android:fillColor">
<gradient android:endX="512" android:endY="256"
android:startX="0" android:startY="256" android:type="linear">
<item android:color="#748AFF" android:offset="0"/>
<item android:color="#FF3C64" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:fillColor="#000" android:pathData="m256,56c-110.281,0 -200,89.719 -200,200s89.719,200 200,200 200,-89.719 200,-200 -89.719,-200 -200,-200zM256,426c-93.738,0 -170,-76.262 -170,-170s76.262,-170 170,-170 170,76.262 170,170 -76.262,170 -170,170zM256,426"/>
<path android:fillColor="#000" android:pathData="m324.18,187.82c-5.859,-5.855 -15.355,-5.855 -21.215,0l-46.965,46.965 -46.965,-46.965c-5.859,-5.855 -15.355,-5.855 -21.215,0 -5.855,5.859 -5.855,15.355 0,21.215l46.965,46.965 -46.965,46.965c-5.855,5.859 -5.855,15.355 0,21.215 2.93,2.93 6.77,4.395 10.605,4.395 3.84,0 7.68,-1.465 10.605,-4.395l46.969,-46.965 46.965,46.965c2.93,2.93 6.77,4.395 10.605,4.395 3.84,0 7.68,-1.465 10.609,-4.395 5.855,-5.859 5.855,-15.355 0,-21.215l-46.965,-46.965 46.965,-46.965c5.855,-5.859 5.855,-15.355 0,-21.215zM324.18,187.82"/>
</vector>

View File

@ -1,32 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector android:height="42dp" android:viewportHeight="200"
android:viewportWidth="200" android:width="42dp" xmlns:android="http://schemas.android.com/apk/res/android">
<group>
<clip-path android:pathData="M100,100m-100,0a100,100 0,1 1,200 0a100,100 0,1 1,-200 0"/>
<path android:fillColor="#E62C28" android:pathData="M202.7,195.2c0,0.3 -0.1,0.8 -0.1,1.1c-1.5,3 -3.7,5.3 -6.9,6.4c-0.2,0 -0.4,0 -0.6,0.1c-0.8,-0.1 -1.6,-0.4 -2.3,-0.4c-61,0 -122.1,0 -183.1,0c-0.7,0 -1.4,0.3 -2.1,0.4c-0.3,0 -0.7,-0.1 -1,-0.1c-3,-1.4 -5.2,-3.6 -6.5,-6.6V6.7c1.3,-3.1 3.5,-5.3 6.6,-6.7C6.8,0 7,0 7.2,0c0.9,0.1 1.7,0.4 2.6,0.4c61,0 122.1,0 183.1,0c0.8,0 1.5,-0.2 2.3,-0.4c0.2,0 0.4,0 0.5,0c3.4,1.2 5.7,3.5 6.9,7c0,0.1 0,0.4 0,0.5c-0.1,0.7 -0.4,1.4 -0.4,2.1c0,61.2 0,122.3 0,183.5C202.3,193.8 202.6,194.5 202.7,195.2zM52.3,178.7c0.9,0 1.6,0 2.3,0c11.3,0 22.6,0.1 33.8,0c2.9,0 6,-0.3 8.8,-0.9c15.9,-3.4 26.8,-12.6 30.8,-28.7c1.8,-7.3 2.8,-14.9 4.1,-22.3c5.5,-31.1 11,-62.2 16.4,-93.2c0.5,-3.1 1.1,-6.1 1.6,-9.4c-1,0 -1.8,0 -2.5,0c-13.4,0 -26.8,-0.1 -40.2,0c-2.9,0 -6,0.3 -8.8,0.9C82,28.6 71.1,38.5 67.6,55.6c-2.1,10 -3.7,20.1 -5.4,30.1c-1.2,6.9 -2.6,13.9 -3.2,20.9c-0.9,10.1 2.7,18.4 11.9,23.6c3.9,2.2 8.2,3.7 12.7,3.9c7.7,0.3 15.4,0.3 23.1,0.4c0.7,0 1.4,0 2.2,0c-0.7,3.6 -1.3,6.8 -1.9,10c-1.6,8.3 -6.1,12.1 -14.5,12.1c-11.3,0 -22.6,0 -33.8,0c-0.8,0 -1.5,0 -2.3,0C54.9,164.1 53.7,171.2 52.3,178.7z"/>
<path android:fillColor="#E94845" android:pathData="M195.2,0c-0.8,0.1 -1.5,0.4 -2.3,0.4c-61,0 -122.1,0 -183.1,0C9,0.4 8.3,0.1 7.5,0C70.1,0 132.6,0 195.2,0z"/>
<path android:fillColor="#E94845" android:pathData="M202.7,195.2c-0.1,-0.7 -0.4,-1.4 -0.4,-2.1c0,-61.2 0,-122.3 0,-183.5c0,-0.7 0.2,-1.4 0.4,-2.1C202.7,70.1 202.7,132.6 202.7,195.2z"/>
<path android:fillColor="#E94845" android:pathData="M7.5,202.7c0.7,-0.1 1.4,-0.4 2.1,-0.4c61,0 122.1,0 183.1,0c0.7,0 1.4,0.3 2.1,0.4C132.4,202.7 69.9,202.7 7.5,202.7z"/>
<path android:fillColor="#FDFCFC" android:pathData="M202.7,7.1c-1.2,-3.5 -3.6,-5.9 -7.1,-7.1h7.1V7.1z"/>
<path android:fillColor="#FDFCFC" android:pathData="M195.6,202.7c3.4,-1.2 5.7,-3.5 7.1,-6.7v6.7H195.6z"/>
<path android:fillColor="#FDFCFC" android:pathData="M0,196c1.3,3.1 3.6,5.3 6.7,6.7H0V196z"/>
<path android:fillColor="#FDFCFC" android:pathData="M6.7,0C3.6,1.4 1.3,3.6 0,6.7V0H6.7z"/>
<path android:fillColor="#FDFCFC" android:pathData="M52.3,178.7c1.3,-7.4 2.6,-14.6 4,-22c0.8,0 1.6,0 2.3,0c11.3,0 22.6,0 33.8,0c8.4,0 12.9,-3.8 14.5,-12.1c0.6,-3.2 1.2,-6.5 1.9,-10c-0.8,0 -1.5,0 -2.2,0c-7.7,-0.1 -15.4,0 -23.1,-0.4c-4.5,-0.2 -8.8,-1.7 -12.7,-3.9c-9.2,-5.2 -12.8,-13.5 -11.9,-23.6c0.6,-7 2,-13.9 3.2,-20.9c1.7,-10.1 3.4,-20.2 5.4,-30.1C71.1,38.5 82,28.6 98.8,25c2.9,-0.6 5.9,-0.9 8.8,-0.9c13.4,-0.1 26.8,0 40.2,0c0.7,0 1.4,0 2.5,0c-0.6,3.3 -1.1,6.3 -1.6,9.4c-5.5,31.1 -11,62.2 -16.4,93.2c-1.3,7.5 -2.3,15 -4.1,22.3c-4,16.1 -14.9,25.3 -30.8,28.7c-2.9,0.6 -5.9,0.9 -8.8,0.9c-11.3,0.1 -22.6,0 -33.8,0C53.9,178.7 53.2,178.7 52.3,178.7zM112.7,112.4c1.2,-6.8 2.4,-13.4 3.5,-20c2.1,-12.1 4.3,-24.3 6.3,-36.4c0.8,-4.6 -1.4,-8 -5.9,-9.3c-1.3,-0.4 -2.7,-0.5 -4.1,-0.5c-3,-0.1 -5.9,0 -8.9,0c-8.2,0 -13.2,4.4 -14.5,12.4c-0.8,4.9 -1.7,9.7 -2.6,14.6c-1.7,9.6 -3.5,19.2 -5,28.8c-0.9,6 2.1,10 8.2,10.3C97.3,112.6 104.9,112.4 112.7,112.4z"/>
<path android:fillColor="#E62D29" android:pathData="M112.7,112.4c-7.8,0 -15.3,0.3 -22.9,-0.1c-6.1,-0.3 -9.2,-4.3 -8.2,-10.3c1.5,-9.6 3.3,-19.2 5,-28.8c0.8,-4.9 1.8,-9.7 2.6,-14.6c1.3,-8 6.3,-12.4 14.5,-12.4c3,0 5.9,-0.1 8.9,0c1.4,0 2.8,0.2 4.1,0.5c4.5,1.3 6.7,4.7 5.9,9.3c-2.1,12.1 -4.2,24.3 -6.3,36.4C115,98.9 113.9,105.5 112.7,112.4z"/>
</group>
</vector>

View File

@ -1,21 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#4EA4FF" android:pathData="M255.968,5.329C114.624,5.329 0,120.401 0,262.353c0,113.536 73.344,209.856 175.104,243.872c12.8,2.368 17.472,-5.568 17.472,-12.384c0,-6.112 -0.224,-22.272 -0.352,-43.712c-71.2,15.52 -86.24,-34.464 -86.24,-34.464c-11.616,-29.696 -28.416,-37.6 -28.416,-37.6c-23.264,-15.936 1.728,-15.616 1.728,-15.616c25.696,1.824 39.2,26.496 39.2,26.496c22.848,39.264 59.936,27.936 74.528,21.344c2.304,-16.608 8.928,-27.936 16.256,-34.368c-56.832,-6.496 -116.608,-28.544 -116.608,-127.008c0,-28.064 9.984,-51.008 26.368,-68.992c-2.656,-6.496 -11.424,-32.64 2.496,-68c0,0 21.504,-6.912 70.4,26.336c20.416,-5.696 42.304,-8.544 64.096,-8.64c21.728,0.128 43.648,2.944 64.096,8.672c48.864,-33.248 70.336,-26.336 70.336,-26.336c13.952,35.392 5.184,61.504 2.56,68c16.416,17.984 26.304,40.928 26.304,68.992c0,98.72 -59.84,120.448 -116.864,126.816c9.184,7.936 17.376,23.616 17.376,47.584c0,34.368 -0.32,62.08 -0.32,70.496c0,6.88 4.608,14.88 17.6,12.352C438.72,472.145 512,375.857 512,262.353C512,120.401 397.376,5.329 255.968,5.329z"/>
</vector>

View File

@ -1,24 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="25dp"
android:height="25dp" android:viewportWidth="512.007" android:viewportHeight="512.007">
<path android:fillColor="#fe646f" android:pathData="m380.125,59.036c-59.77,0 -109.664,42.249 -121.469,98.51 -0.608,2.899 -4.703,2.901 -5.312,0 -11.805,-56.261 -61.699,-98.51 -121.469,-98.51 -114.106,0 -167.756,141.01 -82.508,216.858l193.339,172.02c7.58,6.744 19.009,6.744 26.589,0l193.339,-172.02c85.248,-75.848 31.598,-216.858 -82.509,-216.858z"/>
<path android:fillColor="#fd4755" android:pathData="m380.125,59.036c-6.912,0 -13.689,0.572 -20.293,1.658 99.376,15.991 141.363,144.168 61.527,215.2l-185.996,165.487 7.343,6.533c7.58,6.744 19.009,6.744 26.589,0l193.339,-172.02c85.248,-75.848 31.598,-216.858 -82.509,-216.858z"/>
<path android:fillColor="#fe646f" android:pathData="m380.125,59.036c-59.77,0 -109.664,42.249 -121.469,98.51 -0.608,2.899 -4.703,2.901 -5.312,0 -11.805,-56.261 -61.699,-98.51 -121.469,-98.51 -114.106,0 -167.756,141.01 -82.508,216.858l193.339,172.02c7.58,6.744 19.009,6.744 26.589,0l193.339,-172.02c85.248,-75.848 31.598,-216.858 -82.509,-216.858z"/>
<path android:fillColor="#fd4755" android:pathData="m380.125,59.036c-6.912,0 -13.689,0.572 -20.293,1.658 99.376,15.991 141.363,144.168 61.527,215.2l-185.996,165.487 7.343,6.533c7.58,6.744 19.009,6.744 26.589,0l193.339,-172.02c85.248,-75.848 31.598,-216.858 -82.509,-216.858z"/>
<path android:fillColor="#FF000000" android:pathData="m237.72,453.517c-204.315,-181.786 -197.402,-175.776 -197.402,-175.776 -25.999,-24.984 -40.318,-58.201 -40.318,-93.533 0,-46.48 24.63,-91.702 65.906,-115.47 3.589,-2.067 8.174,-0.833 10.242,2.757 2.067,3.589 0.833,8.175 -2.757,10.242 -36.017,20.74 -58.391,60.004 -58.391,102.471 0,31.212 12.683,60.588 35.711,82.717 0,0 -6.881,-5.996 196.979,175.386 2.292,2.039 5.242,3.161 8.309,3.161 3.066,0 6.018,-1.123 8.31,-3.162l61.917,-55.089c3.095,-2.753 7.835,-2.477 10.588,0.618s2.477,7.835 -0.618,10.588l-61.917,55.09c-10.431,9.281 -26.148,9.263 -36.559,0zM357.363,377.059c-2.067,0 -4.124,-0.849 -5.606,-2.515 -2.753,-3.095 -2.477,-7.835 0.618,-10.588l105.273,-93.665c21.815,-19.409 35.132,-44.369 38.513,-72.181 0.001,-0.006 0.001,-0.012 0.002,-0.018 7.637,-62.927 -37.915,-131.557 -116.038,-131.557 -54.879,0 -102.877,38.923 -114.129,92.55 -1.005,4.79 -5.116,8.135 -9.997,8.135s-8.991,-3.346 -9.996,-8.136c-11.252,-53.626 -59.25,-92.549 -114.128,-92.549 -9.633,0 -19.082,1.076 -28.084,3.198 -4.033,0.952 -8.07,-1.548 -9.021,-5.579 -0.951,-4.032 1.547,-8.07 5.579,-9.021 10.128,-2.388 20.735,-3.598 31.525,-3.598 55.699,0 105.463,35.109 124.125,87.792 18.71,-52.817 68.567,-87.792 124.125,-87.792 84.905,0 139.884,74.56 130.929,148.362 0,0.007 -0.001,0.015 -0.002,0.022 -3.829,31.494 -18.847,59.703 -43.433,81.578l-105.273,93.665c-1.429,1.272 -3.209,1.897 -4.982,1.897z"/>
</vector>

View File

@ -1,6 +0,0 @@
<vector android:height="24dp" android:viewportHeight="456"
android:viewportWidth="456" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M61.179,282h-41.2c-6,0 -10.9,4.9 -10.9,10.9v152.2c0,6 4.9,10.9 10.9,10.9h41.2c6,0 10.9,-4.9 10.9,-10.9V292.9C72.079,286.9 67.179,282 61.179,282z"/>
<path android:fillColor="#FFFFFF" android:pathData="M443.179,294.4c-0.3,-0.4 -0.6,-0.8 -0.8,-1.2c-6.1,-6.8 -16.4,-7.7 -23.6,-2.1c-20,16.3 -49.2,39.8 -68.1,55.1c-16.7,13.3 -37.3,21 -58.7,21.8l-51.2,1.7c-9.4,0.3 -18.2,-4.5 -23,-12.6l-5.7,-9.7c-1.5,-2.5 -2.5,-5.3 -3.1,-8.2c-2.6,-13.9 6.5,-27.4 20.4,-30l52.9,-10c8.5,-1.7 14.3,-9.7 13.3,-18.3c-1,-8.2 -8,-14.3 -16.2,-14.4c-0.3,0 -0.7,0 -0.8,0c-0.4,0 -0.9,0 -1.3,0l-71.2,-2.9c-24.7,-0.9 -46,3.9 -71.2,16.1l-42.8,20.7v114l35.9,-6.6c0.1,-0.1 0.2,-0.1 0.3,-0.1c13.9,-2 23.1,-2.9 38.2,-2.2l107.5,5c33.7,1.4 66.8,-9.7 92.7,-31.3l74.4,-61.7C447.979,311.7 448.879,301.3 443.179,294.4z"/>
<path android:fillColor="#FFFFFF" android:pathData="M307.379,0c-61.2,0.1 -110.7,49.7 -110.8,110.8c0,0.1 0,0.1 0,0.1c0,61.2 49.7,110.8 110.9,110.8s110.8,-49.7 110.8,-110.9S368.579,0 307.379,0zM333.079,80h13.4c5.5,0 10,4.5 10,10s-4.5,10 -10,10h-13.4c-2,7.8 -6,14.9 -11.7,20.6c-7.5,7.5 -17.5,11.9 -28.1,12.5l37.7,35.7c4,3.8 4.2,10.1 0.4,14.1s-10.1,4.2 -14.1,0.4l-55.9,-52.9c-1.9,-1.9 -3.1,-4.5 -3.1,-7.2c-0.1,-5.6 4.4,-10.1 10,-10.2h22.4c6.2,0.1 12.2,-2.3 16.7,-6.7c1.8,-1.9 3.3,-4 4.6,-6.3h-43.7c-5.5,0 -10,-4.5 -10,-10s4.5,-10 10,-10h43.7c-3.7,-8 -11.9,-14 -21.3,-14h-22.4c-5.5,0 -10,-4.5 -10,-10s4.5,-10 10,-10h78.2c5.5,0 10,4.5 10,10s-4.5,10 -10,10h-19.2C330.079,70.3 331.979,75 333.079,80z"/>
</vector>

View File

@ -1,50 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="32dp" android:height="32dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="M352,0H160C71.648,0 0,71.648 0,160v192c0,88.352 71.648,160 160,160h192c88.352,0 160,-71.648 160,-160V160C512,71.648 440.352,0 352,0zM464,352c0,61.76 -50.24,112 -112,112H160c-61.76,0 -112,-50.24 -112,-112V160C48,98.24 98.24,48 160,48h192c61.76,0 112,50.24 112,112V352z">
<aapt:attr name="android:fillColor">
<gradient android:endX="465.1312" android:endY="46.8656"
android:startX="46.8688" android:startY="465.1344" android:type="linear">
<item android:color="#FFFFC107" android:offset="0"/>
<item android:color="#FFF44336" android:offset="0.507"/>
<item android:color="#FF9C27B0" android:offset="0.99"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M256,128c-70.688,0 -128,57.312 -128,128s57.312,128 128,128s128,-57.312 128,-128S326.688,128 256,128zM256,336c-44.096,0 -80,-35.904 -80,-80c0,-44.128 35.904,-80 80,-80s80,35.872 80,80C336,300.096 300.096,336 256,336z">
<aapt:attr name="android:fillColor">
<gradient android:endX="346.5072" android:endY="165.4928"
android:startX="165.4928" android:startY="346.5072" android:type="linear">
<item android:color="#FFFFC107" android:offset="0"/>
<item android:color="#FFF44336" android:offset="0.507"/>
<item android:color="#FF9C27B0" android:offset="0.99"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M393.6,118.4m-17.056,0a17.056,17.056 0,1 1,34.112 0a17.056,17.056 0,1 1,-34.112 0">
<aapt:attr name="android:fillColor">
<gradient android:endX="405.6592" android:endY="106.3408"
android:startX="381.5408" android:startY="130.4624" android:type="linear">
<item android:color="#FFFFC107" android:offset="0"/>
<item android:color="#FFF44336" android:offset="0.507"/>
<item android:color="#FF9C27B0" android:offset="0.99"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -1,8 +0,0 @@
<vector android:height="42dp" android:viewportHeight="250"
android:viewportWidth="488" android:width="81.984dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#fff" android:pathData="M483.73,36A53.1,53.1 0,0 0,452 4.28C438.49,0 425.94,0 400.84,0H325.16C300.07,0 287.52,0 274,4.28A53.08,53.08 0,0 0,242.28 36a76.64,76.64 0,0 0,-2 7.74,140.32 140.32,0 0,1 14,24.86c0.38,-9.57 1.27,-17.22 3.46,-24.14 4.68,-12.86 11.88,-20.06 24.74,-24.74C294.25,16 308.12,16 330,16h66c21.88,0 35.76,0 47.54,3.73 12.86,4.68 20,11.88 24.74,24.74C472,56.25 472,70.13 472,92v66c0,21.88 0,35.76 -3.72,47.53 -4.69,12.86 -11.88,20.06 -24.74,24.74C431.76,234 417.88,234 396,234H330c-21.89,0 -35.76,0 -47.54,-3.73 -12.86,-4.68 -20.06,-11.88 -24.74,-24.74 -2.19,-6.92 -3.09,-14.58 -3.46,-24.15a140.51,140.51 0,0 1,-14 24.85,77.18 77.18,0 0,0 2,7.77A53.08,53.08 0,0 0,274 245.73C287.52,250 300.07,250 325.16,250h75.68c25.1,0 37.65,0 51.16,-4.27A53.11,53.11 0,0 0,483.73 214C488,200.49 488,187.94 488,162.84V87.17C488,62.07 488,49.52 483.73,36Z"/>
<path android:fillColor="#fff" android:pathData="M422,217L380.33,217c-1.76,0 -5.83,-2.79 -2.63,-6.67 21.36,-23 48,-30.93 73.4,-39.42 3.32,-1 3.91,2.51 3.91,3.48v8.68C455,202.61 441.57,217 422,217ZM343.73,212.69c-4,-29.73 -18.06,-80.79 -71,-118.55A3.78,3.78 0,0 1,271 90.63L271,66.36c0,-26.69 18,-33.31 26.37,-33.31a4.3,4.3 0,0 1,4.07 2.1c25.24,55 41,89.86 50.7,172.83 0.05,1.62 0.31,2.39 1.28,0 6.86,-15.07 39.35,-92 26.44,-170.68a3.64,3.64 0,0 1,3.5 -4.25L422,33.05c19.54,0 33,13.43 33,33.36L455,100.5a3.63,3.63 0,0 1,-2.07 3.36,180.12 180.12,0 0,0 -90.3,109.25c-0.79,2.21 -1.25,3.9 -3.71,3.9h-11.8C344.77,217 344.27,216.05 343.73,212.7ZM304.35,217c-20,0 -33.35,-12.37 -33.35,-33.93v-2.24c0,-0.9 0.71,-4.29 4.09,-3.63 20.24,6.23 41.92,12.52 57.77,33.49 1.82,2.56 0.23,6.3 -2.91,6.31Z"/>
<path android:fillColor="#fff" android:pathData="M124.991,239.991a115,115 54.655,1 0,2.007 -229.991a115,115 54.655,1 0,-2.007 229.991z"/>
<path android:fillColor="#2bc5b4" android:pathData="M180.77,114.59c-8.62,0 -15.61,7.39 -15.61,16.49s7,16.5 15.61,16.5 15.62,-7.38 15.62,-16.5S189.4,114.59 180.77,114.59Z"/>
<path android:fillColor="#2bc5b4" android:pathData="M125,0A125,125 0,1 0,250 125,125 125,0 0,0 125,0ZM95.37,132.09c0,63.82 -101.74,35.68 -60.49,2.93 9.65,13.39 28.18,12.5 30.15,-0.72l0.37,-52.05c0.95,-13.32 26.85,-16 30,0ZM133.31,156.32a12.05,12.05 0,0 1,-12 12L116.1,168.32a12.05,12.05 0,0 1,-12 -12L104.1,106a12,12 0,0 1,12 -12h5.21a12,12 0,0 1,12 12ZM133.31,74.56a11.84,11.84 0,0 1,-11.79 11.79L115.9,86.35a11.84,11.84 0,0 1,-11.81 -11.79L104.09,71.65A11.83,11.83 0,0 1,115.9 59.86h5.62a11.82,11.82 0,0 1,11.79 11.79ZM180.77,169.9c-22,0 -39.82,-17.37 -39.82,-38.82s17.84,-38.81 39.82,-38.81 39.81,17.38 39.81,38.81S202.76,169.9 180.77,169.9Z"/>
</vector>

View File

@ -1,33 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="52dp" android:height="52dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="m140.008,423h-30c-11.047,0 -20,-8.953 -20,-20v-186c0,-11.047 8.953,-20 20,-20h30c11.047,0 20,8.953 20,20v186c0,11.047 -8.953,20 -20,20zM166.992,124.996c0,-22.629 -18.359,-40.996 -40.977,-40.996 -22.703,0 -41.016,18.367 -41.016,40.996 0,22.637 18.313,41.004 41.016,41.004 22.617,0 40.977,-18.367 40.977,-41.004zM422,403v-104.336c0,-60.668 -12.816,-105.664 -83.688,-105.664 -34.055,0 -56.914,17.031 -66.246,34.742h-0.066v-10.742c0,-11.047 -8.953,-20 -20,-20h-28c-11.047,0 -20,8.953 -20,20v186c0,11.047 8.953,20 20,20h28c11.047,0 20,-8.953 20,-20v-92.211c0,-29.387 7.48,-57.855 43.906,-57.855 35.93,0 37.094,33.605 37.094,59.723v90.344c0,11.047 8.953,20 20,20h29c11.047,0 20,-8.953 20,-20zM512,432c0,-11.047 -8.953,-20 -20,-20s-20,8.953 -20,20c0,22.055 -17.945,40 -40,40h-352c-22.055,0 -40,-17.945 -40,-40v-352c0,-22.055 17.945,-40 40,-40h352c22.055,0 40,17.945 40,40v251c0,11.047 8.953,20 20,20s20,-8.953 20,-20v-251c0,-44.113 -35.887,-80 -80,-80h-352c-44.113,0 -80,35.887 -80,80v352c0,44.113 35.887,80 80,80h352c44.113,0 80,-35.887 80,-80zM512,432">
<aapt:attr name="android:fillColor">
<gradient android:endX="512" android:endY="256"
android:startX="0" android:startY="256" android:type="linear">
<item android:color="#FF00F2FE" android:offset="0"/>
<item android:color="#FF03EFFE" android:offset="0.0208"/>
<item android:color="#FF24D2FE" android:offset="0.2931"/>
<item android:color="#FF3CBDFE" android:offset="0.5538"/>
<item android:color="#FF4AB0FE" android:offset="0.7956"/>
<item android:color="#FF4FACFE" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -1,29 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="40dp"
android:height="40dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#ff5d7d" android:fillType="evenOdd" android:pathData="m258.229,255.863c-11.191,-11.155 -29.503,-11.155 -40.693,0 -4.486,4.471 -11.053,4.007 -15.072,0 -11.191,-11.155 -29.503,-11.155 -40.693,0 -30.403,30.307 28.128,83.271 48.229,88.64 20.102,-5.369 78.632,-58.333 48.229,-88.64z"/>
<path android:fillColor="#fff" android:fillType="evenOdd" android:pathData="m258.229,255.863c30.403,30.307 -28.128,83.271 -48.23,88.64 -20.102,-5.369 -78.633,-58.334 -48.229,-88.64 11.191,-11.155 29.502,-11.155 40.693,0 4.02,4.007 10.587,4.471 15.072,0 11.191,-11.155 29.503,-11.155 40.694,0zM10,176c0,94.167 60,173.334 80,260h240c3.112,-13.487 7.193,-26.792 11.866,-40 4.742,-13.403 10.093,-26.707 15.66,-40 16.471,-39.33 34.83,-78.563 44.877,-119.994 3.154,-13.009 5.489,-26.235 6.689,-39.749 0.593,-6.679 0.908,-13.429 0.908,-20.257 0,-11 -9,-20 -20,-20 -120,0 -240,0 -360.001,0 -10.999,0 -19.999,9 -19.999,20z"/>
<path android:fillColor="#ccf5fc" android:fillType="evenOdd" android:pathData="m402,356h-44.474c-5.567,13.293 -10.918,26.597 -15.66,40h60.134c55,0 99.999,-45 99.999,-100 0,-52.616 -41.185,-96.074 -92.908,-99.743 -1.2,13.514 -3.534,26.74 -6.69,39.749 32.818,0.218 59.599,27.129 59.599,59.994 0,33 -27,60 -60,60z"/>
<path android:fillColor="#ccf5fc" android:fillType="evenOdd" android:pathData="m330,436h-240,-20c-11,0 -20,9 -20,20s9,20 20,20h280c11,0 20,-9 20,-20s-9,-20 -20,-20z"/>
<path android:fillColor="#FF000000" android:pathData="m419.714,187.451c0.186,-3.793 0.286,-7.608 0.286,-11.451 0,-16.542 -13.458,-30 -30,-30h-360c-16.542,0 -30,13.458 -30,30 0,59.097 22.691,112.205 44.635,163.564 12.597,29.484 24.571,57.526 32.514,86.436h-7.149c-16.542,0 -30,13.458 -30,30s13.458,30 30,30h280c16.542,0 30,-13.458 30,-30s-13.458,-30 -30,-30h-7.147c1.842,-6.704 3.897,-13.362 6.13,-20h53.017c60.654,0 110,-49.346 110,-110 0,-53.968 -39.847,-99.962 -92.286,-108.549zM409.978,246.658c23.725,3.87 42.022,24.684 42.022,49.342 0,27.57 -22.43,50 -50,50h-29.383c0.912,-2.138 1.828,-4.282 2.747,-6.435 12.854,-30.084 25.958,-60.771 34.614,-92.907zM254.997,426c-5.523,0 -10,4.478 -10,10s4.477,10 10,10h95.003c5.514,0 10,4.486 10,10s-4.486,10 -10,10h-280c-5.514,0 -10,-4.486 -10,-10s4.486,-10 10,-10h94.997c5.523,0 10,-4.478 10,-10s-4.477,-10 -10,-10h-67.153c-8.334,-32.299 -21.78,-63.781 -34.817,-94.293 -21.153,-49.509 -43.027,-100.704 -43.027,-155.707 0,-5.514 4.486,-10 10,-10h360c5.514,0 10,4.486 10,10 0,55.003 -21.874,106.198 -43.027,155.707 -13.036,30.513 -26.486,61.997 -34.82,94.293zM402,386h-45.791c2.546,-6.646 5.221,-13.303 7.988,-20h37.803c38.599,0 70,-31.401 70,-70 0,-34.024 -24.884,-62.818 -57.401,-68.83 1.329,-6.513 2.447,-13.09 3.312,-19.739 42.202,7.615 74.089,44.901 74.089,88.569 0,49.626 -40.374,90 -90,90z"/>
<path android:fillColor="#FF000000" android:pathData="m210.476,248.781c-0.2,0.201 -0.476,0.477 -0.953,0v0.001c-15.113,-15.066 -39.703,-15.066 -54.813,0 -10.553,10.519 -13.958,24.203 -9.847,39.572 8.313,31.073 45.551,61.27 62.555,65.811 0.845,0.226 1.713,0.339 2.581,0.339s1.735,-0.113 2.581,-0.339c17.004,-4.541 54.242,-34.736 62.556,-65.811 4.111,-15.369 0.706,-29.054 -9.846,-39.572 -15.113,-15.065 -39.702,-15.066 -54.814,-0.001zM255.815,283.185c-5.882,21.986 -33.302,45.229 -45.815,50.721 -12.513,-5.491 -39.933,-28.734 -45.814,-50.721 -2.249,-8.407 -0.773,-14.838 4.646,-20.239 3.663,-3.651 8.474,-5.478 13.286,-5.478s9.624,1.826 13.288,5.478v0.001c8.185,8.156 21.007,8.157 29.191,-0.001 7.326,-7.303 19.247,-7.303 26.574,0 5.417,5.401 6.892,11.832 4.644,20.239z"/>
<path android:fillColor="#ccf5fc" android:pathData="m201.736,110.504c-3.034,4.615 -1.752,10.815 2.862,13.85 1.693,1.113 3.599,1.646 5.484,1.646 3.253,0 6.444,-1.586 8.365,-4.507 17.816,-27.099 6.822,-41.619 -0.453,-51.228 -6.372,-8.416 -9.882,-13.052 0.453,-28.771 3.034,-4.615 1.752,-10.815 -2.862,-13.85 -4.614,-3.034 -10.815,-1.753 -13.85,2.861 -18.093,27.52 -7.016,42.15 0.314,51.832 6.489,8.57 9.745,12.871 -0.313,28.167z"/>
<path android:fillColor="#ccf5fc" android:pathData="m121.733,110.504c-3.034,4.615 -1.752,10.815 2.862,13.85 1.693,1.113 3.599,1.646 5.484,1.646 3.253,0 6.444,-1.586 8.365,-4.507 17.816,-27.099 6.823,-41.619 -0.452,-51.228 -6.373,-8.416 -9.882,-13.053 0.452,-28.771 3.034,-4.615 1.752,-10.815 -2.862,-13.85 -4.614,-3.034 -10.816,-1.753 -13.85,2.861 -18.093,27.52 -7.016,42.15 0.314,51.831 6.489,8.571 9.746,12.872 -0.313,28.168z"/>
<path android:fillColor="#ccf5fc" android:pathData="m281.739,110.504c-3.034,4.615 -1.753,10.815 2.861,13.85 1.693,1.113 3.6,1.646 5.484,1.646 3.254,0 6.444,-1.585 8.365,-4.507 17.817,-27.099 6.823,-41.619 -0.452,-51.228 -6.372,-8.416 -9.882,-13.053 0.452,-28.771 3.034,-4.615 1.753,-10.815 -2.861,-13.85 -4.615,-3.034 -10.815,-1.751 -13.85,2.861 -18.094,27.52 -7.017,42.15 0.313,51.831 6.49,8.571 9.746,12.872 -0.312,28.168z"/>
<path android:fillColor="#FF000000" android:pathData="m210,426h-0.007c-5.523,0 -9.996,4.478 -9.996,10s4.48,10 10.003,10 10,-4.478 10,-10 -4.477,-10 -10,-10z"/>
</vector>

View File

@ -1,28 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="300dp"
android:height="300dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#A3787878" android:pathData="m256,80a48.054,48.054 0,0 1,48 48v32h12a19.991,19.991 0,0 0,3.524 -39.671,63.984 63.984,0 0,0 -127.048,0 19.991,19.991 0,0 0,3.524 39.671h12v-32a48.054,48.054 0,0 1,48 -48z"/>
<path android:fillColor="#A3787878" android:pathData="m48,152a24.027,24.027 0,0 0,24 -24v-74.234l42.53,-14.176 -5.06,-15.18 -48,16a8,8 0,0 0,-5.47 7.59v57.376a24,24 0,1 0,-8 46.624zM48,120a8,8 0,1 1,-8 8,8.009 8.009,0 0,1 8,-8z"/>
<path android:fillColor="#A3787878" android:pathData="m485.006,17.76a7.993,7.993 0,0 0,-6.741 -1.569l-72,16a8,8 0,0 0,-6.265 7.809v57.376a24,24 0,1 0,16 22.624v-73.583l56,-12.444v47.4a24,24 0,1 0,16 22.627v-80a8,8 0,0 0,-2.994 -6.24zM392,128a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8zM464,112a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8z"/>
<path android:fillColor="#A3787878" android:pathData="m48,456h416v40h-416z"/>
<path android:fillColor="#A3787878" android:pathData="m64,376a16,16 0,0 0,-16 16v7h48v-7a16,16 0,0 0,-16 -16z"/>
<path android:fillColor="#A3787878" android:pathData="m24,416h464v24h-464z"/>
<path android:fillColor="#A3787878" android:pathData="M256,144m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
<path android:fillColor="#A3787878" android:pathData="m368,400 l16,-160h-256l16,160zM256,296a24,24 0,1 1,-24 24,24 24,0 0,1 24,-24z"/>
<path android:fillColor="#A3787878" android:pathData="m168,224h176a32,32 0,0 0,-32 -32h-112a32,32 0,0 0,-32 32z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:viewportHeight="64"
android:viewportWidth="64" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#BBFFFFFF" android:fillType="evenOdd" android:pathData="M52.402,31.916c0,4.03 -1.17,7.895 -3.178,11.087l8.196,8.23c4.014,-5.375 6.523,-12.094 6.523,-19.318s-2.51,-13.942 -6.523,-19.318l-8.196,8.23c2.007,3.192 3.178,6.887 3.178,11.087z"/>
<path android:fillColor="#FFFFFF" android:fillType="evenOdd" android:pathData="M32.004,52.41c-11.207,0 -20.406,-9.24 -20.406,-20.493s9.2,-20.493 20.406,-20.493c4.182,0 7.86,1.176 11.04,3.36l8.196,-8.23C45.887,2.52 39.197,0 32.004,0 14.44,0 0.057,14.278 0.057,32.084S14.44,64 32.004,64c7.36,0 14.05,-2.52 19.403,-6.55l-8.196,-8.23c-3.178,2.016 -7.025,3.192 -11.207,3.192z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:viewportHeight="435.505"
android:viewportWidth="435.505" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M403.496,101.917c-4.104,-5.073 -8.877,-9.705 -14.166,-13.839c0.707,13.117 -0.508,27.092 -3.668,41.884c-8.627,40.413 -29.256,74.754 -59.656,99.304c-30.375,24.533 -68.305,37.502 -109.686,37.502h-60.344l-19.533,91.512c-3.836,17.959 -19.943,30.99 -38.303,30.99H70.938l-4.898,22.484c-1.258,5.79 0.17,11.839 3.887,16.453c3.715,4.614 9.324,7.298 15.25,7.298h66.498c9.24,0 17.225,-6.459 19.152,-15.495L193.667,313h76.188c36.854,0 70.527,-11.464 97.384,-33.152c26.869,-21.697 45.129,-52.186 52.807,-88.162C427.822,155.309 422.253,125.106 403.496,101.917z"/>
<path android:fillColor="#FFFFFF" android:pathData="M117.292,354.191l22.84,-107.008h76.188c36.852,0 70.527,-11.465 97.383,-33.154c26.867,-21.697 45.129,-52.186 52.809,-88.161c7.773,-36.378 2.207,-66.58 -16.553,-89.769C331.952,13.832 301.17,0 269.633,0H103.639c-9.209,0 -17.174,6.417 -19.135,15.414L12.505,345.938c-1.26,5.789 0.168,11.838 3.887,16.453c3.713,4.613 9.32,7.296 15.248,7.296h66.5C107.38,369.687 115.36,363.229 117.292,354.191zM178.235,75.291h52.229c12.287,0 23.274,5.149 30.145,14.129c7.297,9.539 9.431,22.729 5.853,36.188c-0.047,0.171 -0.088,0.342 -0.131,0.516c-6.57,27.73 -33.892,50.291 -60.898,50.291h-50.05L178.235,75.291z"/>
</vector>

View File

@ -1,31 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
android:height="42dp" android:viewportWidth="496" android:viewportHeight="496">
<path android:fillColor="#6C9DFF" android:pathData="M248,92c-13.6,0 -24,-10.4 -24,-24V24c0,-13.6 10.4,-24 24,-24s24,10.4 24,24v44C272,80.8 261.6,92 248,92z"/>
<path android:fillColor="#DA3B7A" android:pathData="M248,496c-13.6,0 -24,-10.4 -24,-24v-44c0,-13.6 10.4,-24 24,-24s24,10.4 24,24v44C272,485.6 261.6,496 248,496z"/>
<path android:fillColor="#63BBFF" android:pathData="M157.6,116c-8,0 -16,-4 -20.8,-12l-21.6,-37.6c-6.4,-11.2 -2.4,-26.4 8.8,-32.8s26.4,-2.4 32.8,8.8L178.4,80c6.4,11.2 2.4,26.4 -8.8,32.8C166.4,114.4 161.6,116 157.6,116z"/>
<path android:fillColor="#E542A9" android:pathData="M360,465.6c-8,0 -16,-4 -20.8,-12L317.6,416c-6.4,-11.2 -2.4,-26.4 8.8,-32.8c11.2,-6.4 26.4,-2.4 32.8,8.8l21.6,37.6c6.4,11.2 2.4,26.4 -8.8,32.8C368,464.8 364,465.6 360,465.6z"/>
<path android:fillColor="#A1DCEC" android:pathData="M92,181.6c-4,0 -8,-0.8 -12,-3.2l-37.6,-21.6c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8s21.6,-15.2 32.8,-8.8l37.6,21.6c11.2,6.4 15.2,21.6 8.8,32.8C108,177.6 100,181.6 92,181.6z"/>
<path android:fillColor="#B135FF" android:pathData="M442.4,384c-4,0 -8,-0.8 -12,-3.2L392,359.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8c6.4,-11.2 21.6,-15.2 32.8,-8.8l37.6,21.6c11.2,6.4 15.2,21.6 8.8,32.8C458.4,380 450.4,384 442.4,384z"/>
<path android:fillColor="#F3FFFD" android:pathData="M68,272H24c-13.6,0 -24,-10.4 -24,-24s10.4,-24 24,-24h44c13.6,0 24,10.4 24,24S80.8,272 68,272z"/>
<path android:fillColor="#9254C8" android:pathData="M472,272h-44c-13.6,0 -24,-10.4 -24,-24s10.4,-24 24,-24h44c13.6,0 24,10.4 24,24S485.6,272 472,272z"/>
<path android:fillColor="#CE1CFF" android:pathData="M53.6,384c-8,0 -16,-4 -20.8,-12c-6.4,-11.2 -2.4,-26.4 8.8,-32.8l37.6,-21.6c11.2,-6.4 26.4,-2.4 32.8,8.8c6.4,11.2 2.4,26.4 -8.8,32.8l-37.6,21.6C62.4,383.2 58.4,384 53.6,384z"/>
<path android:fillColor="#6953E5" android:pathData="M404,181.6c-8,0 -16,-4 -20.8,-12c-6.4,-11.2 -2.4,-26.4 8.8,-32.8l37.6,-21.6c11.2,-6.4 26.4,-2.4 32.8,8.8s2.4,26.4 -8.8,32.8L416,178.4C412,180.8 408,181.6 404,181.6z"/>
<path android:fillColor="#DE339F" android:pathData="M136,465.6c-4,0 -8,-0.8 -12,-3.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8l21.6,-37.6c6.4,-11.2 21.6,-15.2 32.8,-8.8c11.2,6.4 15.2,21.6 8.8,32.8l-21.6,37.6C152,461.6 144,465.6 136,465.6z"/>
<path android:fillColor="#5681FF" android:pathData="M338.4,116c-4,0 -8,-0.8 -12,-3.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8l21.6,-37.6c6.4,-11.2 21.6,-15.2 32.8,-8.8c11.2,6.4 15.2,21.6 8.8,32.8L359.2,104C354.4,111.2 346.4,116 338.4,116z"/>
</vector>

View File

@ -1,26 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM16.3,16.3c-0.39,0.39 -1.02,0.39 -1.41,0L12,13.41 9.11,16.3c-0.39,0.39 -1.02,0.39 -1.41,0 -0.39,-0.39 -0.39,-1.02 0,-1.41L10.59,12 7.7,9.11c-0.39,-0.39 -0.39,-1.02 0,-1.41 0.39,-0.39 1.02,-0.39 1.41,0L12,10.59l2.89,-2.89c0.39,-0.39 1.02,-0.39 1.41,0 0.39,0.39 0.39,1.02 0,1.41L13.41,12l2.89,2.89c0.38,0.38 0.38,1.02 0,1.41z"/>
</vector>

View File

@ -1,22 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp"
android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#FF3C64" android:pathData="m304,232a24,24 0,0 1,-16.971 -40.971l160,-160a24,24 0,0 1,33.942 33.942l-160,160a23.926,23.926 0,0 1,-16.971 7.029z"/>
<path android:fillColor="#FF3B63" android:pathData="m464,200a24,24 0,0 1,-24 -24v-104h-104a24,24 0,0 1,0 -48h128a24,24 0,0 1,24 24v128a24,24 0,0 1,-24 24z"/>
<path android:fillColor="#CE1CFF" android:pathData="m464,488h-416a24,24 0,0 1,-24 -24v-416a24,24 0,0 1,24 -24h176a24,24 0,0 1,0 48h-152v368h368v-152a24,24 0,0 1,48 0v176a24,24 0,0 1,-24 24z"/>
</vector>

View File

@ -1,21 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
android:height="42dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#A3787878" android:pathData="m511.739,103.734 l-257,50.947v233.725c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-182.682l197,-39.053v98.141c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c39.927,0 71.547,-34.762 67.073,-75h0.427zM217.239,482c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM444.239,422c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM481.739,199.682 L284.739,238.735v-59.416l197,-39.053z"/>
<path android:fillColor="#A3787878" android:pathData="m182.179,159.75h30c0,-31.002 4.415,-66.799 -24.144,-95.356 -8.968,-8.968 -17.455,-16.07 -24.942,-22.336 -19.798,-16.57 -27.832,-24.012 -27.832,-42.058h-30v221.406c-10.734,-7.199 -23.634,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-227.219c9.458,8.262 20.077,16.341 31.562,27.825 19.029,19.031 15.356,44.009 15.356,74.144zM67.761,315c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.823,37.5 -37.5,37.5z"/>
</vector>

View File

@ -1,76 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:gravity="center"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.10546875"
android:scaleY="0.10546875"
android:translateX="27"
android:translateY="27">
<path
android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
<aapt:attr name="android:fillColor">
<gradient
android:startY="437.0193"
android:startX="74.9807"
android:endY="74.9807"
android:endX="437.0193"
android:type="linear">
<item android:offset="0" android:color="#FF736BFD"/>
<item android:offset="1" android:color="#FFF54187"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FF000000"
android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
<path
android:fillColor="#FF000000"
android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
<path
android:fillColor="#FF000000"
android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
<path
android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="386.3741"
android:startX="487.8323"
android:endY="490.009"
android:endX="384.1974"
android:type="linear">
<item android:offset="0" android:color="#FF736BFD"/>
<item android:offset="1" android:color="#FFF54187"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FF000000"
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
android:strokeWidth="0.75"
android:strokeColor="#000000"/>
<path
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
android:strokeWidth="6"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
</group>
</vector>

View File

@ -1,20 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
android:height="42dp" android:viewportWidth="427.652" android:viewportHeight="427.652">
<path android:fillColor="#00D95F" android:pathData="M213.826,0C95.733,0 0,95.733 0,213.826s95.733,213.826 213.826,213.826s213.826,-95.733 213.826,-213.826S331.919,0 213.826,0zM306.886,310.32c-2.719,4.652 -7.612,7.246 -12.638,7.247c-2.506,0 -5.044,-0.645 -7.364,-2c-38.425,-22.456 -82.815,-26.065 -113.295,-25.138c-33.763,1.027 -58.523,7.692 -58.769,7.76c-7.783,2.126 -15.826,-2.454 -17.961,-10.236c-2.134,-7.781 2.43,-15.819 10.209,-17.962c1.116,-0.307 27.76,-7.544 64.811,-8.766c21.824,-0.72 42.834,0.801 62.438,4.52c24.83,4.71 47.48,12.978 67.322,24.574C308.612,294.393 310.96,303.349 306.886,310.32zM334.07,253.861c-3.22,5.511 -9.016,8.583 -14.97,8.584c-2.968,0 -5.975,-0.763 -8.723,-2.369c-45.514,-26.6 -98.097,-30.873 -134.2,-29.776c-39.994,1.217 -69.323,9.112 -69.614,9.192c-9.217,2.515 -18.746,-2.906 -21.275,-12.124c-2.528,-9.218 2.879,-18.738 12.093,-21.277c1.322,-0.364 32.882,-8.937 76.77,-10.384c25.853,-0.852 50.739,0.949 73.96,5.354c29.412,5.58 56.241,15.373 79.744,29.108C336.115,234.995 338.897,245.603 334.07,253.861zM350.781,202.526c-3.641,0 -7.329,-0.936 -10.7,-2.906c-108.207,-63.238 -248.572,-25.643 -249.977,-25.255c-11.313,3.117 -23.008,-3.527 -26.124,-14.839c-3.117,-11.312 3.527,-23.008 14.839,-26.124c1.621,-0.447 40.333,-10.962 94.166,-12.737c31.713,-1.044 62.237,1.164 90.72,6.567c36.077,6.844 68.987,18.856 97.815,35.704c10.13,5.92 13.543,18.931 7.623,29.061C365.193,198.757 358.084,202.526 350.781,202.526z"/>
</vector>

View File

@ -1,30 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="36dp" android:height="36dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="m512,256c0,141.387 -114.613,256 -256,256s-256,-114.613 -256,-256 114.613,-256 256,-256 256,114.613 256,256zM512,256">
<aapt:attr name="android:fillColor">
<gradient android:endX="512" android:endY="256"
android:startX="0" android:startY="256" android:type="linear">
<item android:color="#748AFF" android:offset="0"/>
<item android:color="#FF3C64" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:fillColor="#000000" android:pathData="m175,395.246c-4.035,0 -7.902,-1.629 -10.727,-4.512l-81,-82.832c-5.789,-5.922 -5.684,-15.418 0.238,-21.211 5.922,-5.793 15.418,-5.688 21.211,0.238l70.273,71.859 232.277,-237.523c5.793,-5.922 15.289,-6.027 21.211,-0.234 5.926,5.789 6.031,15.289 0.238,21.211l-243,248.492c-2.82,2.883 -6.688,4.512 -10.723,4.512zM175,395.246"/>
</vector>

View File

@ -1,23 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector android:height="42dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="42dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#E11F23" android:pathData="M236.966,236.966m-236.966,0a236.966,236.966 0,1 1,473.932 0a236.966,236.966 0,1 1,-473.932 0"/>
<path android:fillColor="#E11F23" android:pathData="M404.518,69.38c92.541,92.549 92.549,242.593 0,335.142c-92.541,92.541 -242.593,92.545 -335.142,0L404.518,69.38z"/>
<path android:fillColor="#E11F23" android:pathData="M469.168,284.426L351.886,167.148l-138.322,15.749l-83.669,129.532l156.342,156.338C378.157,449.322 450.422,376.612 469.168,284.426z"/>
<path android:fillColor="#FFFFFF" android:pathData="M360.971,191.238c0,-19.865 -16.093,-35.966 -35.947,-35.966H156.372c-19.85,0 -35.94,16.105 -35.94,35.966v96.444c0,19.865 16.093,35.966 35.94,35.966h168.649c19.858,0 35.947,-16.105 35.947,-35.966v-96.444H360.971zM216.64,280.146v-90.584l68.695,45.294L216.64,280.146z"/>
</vector>

View File

@ -1,22 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector android:height="44dp" android:viewportHeight="192"
android:viewportWidth="192" android:width="44dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF0000" android:pathData="M96,96m-88,0a88,88 0,1 1,176 0a88,88 0,1 1,-176 0"/>
<path android:fillColor="#FFFFFF" android:pathData="M96,54.04c23.14,0 41.96,18.82 41.96,41.96S119.14,137.96 96,137.96S54.04,119.14 54.04,96S72.86,54.04 96,54.04M96,50c-25.41,0 -46,20.59 -46,46s20.59,46 46,46s46,-20.59 46,-46S121.41,50 96,50L96,50z"/>
<path android:fillColor="#FFFFFF" android:pathData="M80,119l39,-24l-39,-22z"/>
</vector>

View File

@ -1,21 +0,0 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
android:height="42dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#A3787878" android:pathData="m511.739,103.734 l-257,50.947v233.725c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-182.682l197,-39.053v98.141c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c39.927,0 71.547,-34.762 67.073,-75h0.427zM217.239,482c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM444.239,422c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM481.739,199.682 L284.739,238.735v-59.416l197,-39.053z"/>
<path android:fillColor="#A3787878" android:pathData="m182.179,159.75h30c0,-31.002 4.415,-66.799 -24.144,-95.356 -8.968,-8.968 -17.455,-16.07 -24.942,-22.336 -19.798,-16.57 -27.832,-24.012 -27.832,-42.058h-30v221.406c-10.734,-7.199 -23.634,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-227.219c9.458,8.262 20.077,16.341 31.562,27.825 19.029,19.031 15.356,44.009 15.356,74.144zM67.761,315c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.823,37.5 -37.5,37.5z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -13,8 +13,6 @@ android {
minSdk = Versions.minSdkVersion
targetSdk = Versions.targetSdkVersion
// versionCode = Versions.versionCode
// versionName = Versions.versionName
/*ndk {
abiFilters.addAll(setOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a"))

View File

@ -2,6 +2,7 @@ package nl.bravobit.ffmpeg;
import android.os.Build;
@SuppressWarnings("deprecation")
public class CpuArchHelper {
public static final String X86_CPU = "x86";
public static final String X86_64_CPU = "x86_64";

View File

@ -1,7 +1,6 @@
package nl.bravobit.ffmpeg;
import android.os.AsyncTask;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -9,10 +8,12 @@ import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.TimeoutException;
@SuppressWarnings("deprecation")
class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> implements FFtask {
private final String[] cmd;
private Map<String, String> environment;
private final Map<String, String> environment;
private final StringBuilder outputStringBuilder = new StringBuilder();
private final FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler;
private final ShellCommand shellCommand;
private final long timeout;
@ -39,6 +40,7 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
@Override
protected CommandResult doInBackground(Void... params) {
CommandResult ret = CommandResult.getDummyFailureResponse();
try {
process = shellCommand.run(cmd, environment);
if (process == null) {
@ -46,16 +48,19 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
}
Log.d("Running publishing updates method");
checkAndUpdateProcess();
return CommandResult.getOutputFromProcess(process);
ret = CommandResult.getOutputFromProcess(process);
outputStringBuilder.append(ret.output);
} catch (TimeoutException e) {
Log.e("FFmpeg binary timed out", e);
return new CommandResult(false, e.getMessage());
ret = new CommandResult(false, e.getMessage());
outputStringBuilder.append(ret.output);
} catch (Exception e) {
Log.e("Error running FFmpeg binary", e);
} finally {
Util.destroyProcess(process);
}
return CommandResult.getDummyFailureResponse();
output = outputStringBuilder.toString();
return ret;
}
@Override
@ -68,7 +73,6 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
@Override
protected void onPostExecute(CommandResult commandResult) {
if (ffmpegExecuteResponseHandler != null) {
output += commandResult.output;
if (commandResult.success) {
ffmpegExecuteResponseHandler.onSuccess(output);
} else {
@ -107,7 +111,7 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
return;
}
output += line + "\n";
outputStringBuilder.append(line); outputStringBuilder.append("\n");
publishProgress(line);
}
} catch (IOException e) {
@ -139,4 +143,4 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
e.printStackTrace();
}
}
}
}

View File

@ -6,6 +6,7 @@ import android.os.AsyncTask;
import java.io.File;
import java.util.Map;
@SuppressWarnings("deprecation")
public class FFprobe implements FFbinaryInterface {
private final FFbinaryContextProvider context;
@ -22,12 +23,7 @@ public class FFprobe implements FFbinaryInterface {
public static FFprobe getInstance(final Context context) {
if (instance == null) {
instance = new FFprobe(new FFbinaryContextProvider() {
@Override
public Context provide() {
return context;
}
});
instance = new FFprobe(() -> context);
}
return instance;
}

Some files were not shown because too many files have changed in this diff Show More