mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 20:57:54 +01:00
Merge pull request #668 from Shabinder/sound-cloud
Sound cloud Support and More
This commit is contained in:
commit
c760385727
@ -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")
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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 }
|
||||
|
@ -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
BIN
art/SpotiFlyer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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
135
buildSrc/deps.versions.toml
Normal 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"]
|
12
buildSrc/settings.gradle.kts
Normal file
12
buildSrc/settings.gradle.kts
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
enableFeaturePreview("VERSION_CATALOGS")
|
||||
dependencyResolutionManagement {
|
||||
@Suppress("UnstableApiUsage")
|
||||
versionCatalogs {
|
||||
create("deps") {
|
||||
from(files("deps.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "spotiflyer-build"
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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") {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -58,6 +58,9 @@ expect fun SpotifyLogo(): Painter
|
||||
@Composable
|
||||
expect fun SaavnLogo(): Painter
|
||||
|
||||
@Composable
|
||||
expect fun SoundCloudLogo(): Painter
|
||||
|
||||
@Composable
|
||||
expect fun YoutubeLogo(): Painter
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
@ -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()
|
||||
)
|
@ -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"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.shabinder.common.models.soundcloud
|
||||
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Media(
|
||||
val transcodings: List<Transcoding> = emptyList()
|
||||
)
|
@ -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 = ""
|
||||
)
|
@ -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 = ""
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
@ -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 = ""
|
||||
)
|
@ -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()
|
||||
)
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,5 +20,6 @@ enum class Source {
|
||||
Spotify,
|
||||
YouTube,
|
||||
Gaana,
|
||||
JioSaavn
|
||||
JioSaavn,
|
||||
SoundCloud
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ val globalJson by lazy {
|
||||
Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
coerceInputValues = true
|
||||
}
|
||||
}
|
||||
|
||||
|
14
common/data-models/src/main/res/drawable/ic_soundcloud.xml
Normal file
14
common/data-models/src/main/res/drawable/ic_soundcloud.xml
Normal 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>
|
BIN
common/data-models/src/main/res/drawable/spotiflyer.png
Normal file
BIN
common/data-models/src/main/res/drawable/spotiflyer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -30,7 +30,6 @@ kotlin {
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:providers"))
|
||||
implementation(project(":common:core-components"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -30,7 +30,6 @@ kotlin {
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:providers"))
|
||||
implementation(project(":common:core-components"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ kotlin {
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:core-components"))
|
||||
implementation(project(":common:providers"))
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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()) }
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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,
|
||||
|
@ -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++
|
||||
}
|
||||
|
||||
|
@ -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(".")
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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()}",
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
|
@ -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++
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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"))
|
||||
|
@ -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";
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user