mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +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 com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
|
||||||
import org.jetbrains.compose.compose
|
import org.jetbrains.compose.compose
|
||||||
import org.jetbrains.kotlin.kapt.cli.main
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
@ -79,6 +78,9 @@ android {
|
|||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
packagingOptions {
|
||||||
|
exclude("META-INF/*")
|
||||||
|
}
|
||||||
configurations {
|
configurations {
|
||||||
"implementation" {
|
"implementation" {
|
||||||
exclude(group = "androidx.compose.animation")
|
exclude(group = "androidx.compose.animation")
|
||||||
@ -92,7 +94,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Androidx.androidxActivity)
|
implementation(deps.androidx.activity)
|
||||||
|
|
||||||
// Project's SubModules
|
// Project's SubModules
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
@ -103,43 +105,40 @@ dependencies {
|
|||||||
implementation(project(":common:core-components"))
|
implementation(project(":common:core-components"))
|
||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
|
|
||||||
|
with(deps) {
|
||||||
|
|
||||||
// Koin
|
// Koin
|
||||||
implementation(Koin.android)
|
with(koin) {
|
||||||
implementation(Koin.compose)
|
implementation(androidx.compose)
|
||||||
|
implementation(android)
|
||||||
|
}
|
||||||
|
|
||||||
// DECOMPOSE
|
// DECOMPOSE
|
||||||
implementation(Decompose.decompose)
|
with(decompose) {
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(dep)
|
||||||
|
implementation(extensions.compose)
|
||||||
// MVI
|
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
implementation(MVIKotlin.mvikotlinMain)
|
|
||||||
implementation(MVIKotlin.mvikotlinLogging)
|
|
||||||
implementation(MVIKotlin.mvikotlinTimeTravel)
|
|
||||||
|
|
||||||
// Extras
|
|
||||||
with(Extras.Android) {
|
|
||||||
implementation(countly)
|
|
||||||
implementation(appUpdator)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
with(Versions.androidxLifecycle) {
|
implementation(countly.android)
|
||||||
implementation("androidx.lifecycle:lifecycle-service:$this")
|
implementation(android.app.notifier)
|
||||||
implementation("androidx.lifecycle:lifecycle-common-java8:$this")
|
implementation(storage.chooser)
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$this")
|
|
||||||
}
|
|
||||||
|
|
||||||
implementation(Extras.kermit)
|
with(bundles) {
|
||||||
// implementation("com.jakewharton.timber:timber:4.7.1")
|
implementation(ktor)
|
||||||
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
|
implementation(mviKotlin)
|
||||||
implementation("com.github.shabinder:storage-chooser:2.0.4.45")
|
implementation(androidx.lifecycle)
|
||||||
implementation("com.google.accompanist:accompanist-insets:0.16.1")
|
implementation(accompanist.inset)
|
||||||
|
}
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation(junit)
|
||||||
androidTestImplementation(Androidx.junit)
|
androidTestImplementation(androidx.junit)
|
||||||
androidTestImplementation(Androidx.expresso)
|
androidTestImplementation(androidx.expresso)
|
||||||
|
|
||||||
// Desugaring
|
// Desugar
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
coreLibraryDesugaring(androidx.desugar)
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
debugImplementation(leak.canary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<package android:name="com.gaana" />
|
<package android:name="com.gaana" />
|
||||||
<package android:name="com.spotify.music" />
|
<package android:name="com.spotify.music" />
|
||||||
<package android:name="com.jio.media.jiobeats" />
|
<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.youtube" />
|
||||||
<package android:name="com.google.android.apps.youtube.music" />
|
<package android:name="com.google.android.apps.youtube.music" />
|
||||||
</queries>
|
</queries>
|
||||||
|
@ -43,6 +43,7 @@ import com.shabinder.common.models.event.coroutines.failure
|
|||||||
import com.shabinder.common.providers.FetchPlatformQueryResult
|
import com.shabinder.common.providers.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -62,6 +63,7 @@ class ForegroundService : LifecycleService() {
|
|||||||
private val fetcher: FetchPlatformQueryResult by inject()
|
private val fetcher: FetchPlatformQueryResult by inject()
|
||||||
private val logger: Kermit by inject()
|
private val logger: Kermit by inject()
|
||||||
private val dir: FileManager by inject()
|
private val dir: FileManager by inject()
|
||||||
|
private val httpClient: HttpClient by inject()
|
||||||
|
|
||||||
private var messageList =
|
private var messageList =
|
||||||
java.util.Collections.synchronizedList(MutableList(5) { emptyMessage })
|
java.util.Collections.synchronizedList(MutableList(5) { emptyMessage })
|
||||||
@ -170,7 +172,7 @@ class ForegroundService : LifecycleService() {
|
|||||||
trackStatusFlowMap[track.title] = DownloadStatus.Downloading()
|
trackStatusFlowMap[track.title] = DownloadStatus.Downloading()
|
||||||
|
|
||||||
// Enqueueing Download
|
// Enqueueing Download
|
||||||
downloadFile(url).collect {
|
httpClient.downloadFile(url).collect {
|
||||||
when (it) {
|
when (it) {
|
||||||
is DownloadResult.Error -> {
|
is DownloadResult.Error -> {
|
||||||
logger.d(TAG) { it.message }
|
logger.d(TAG) { it.message }
|
||||||
|
@ -15,12 +15,19 @@ fun cleanFiles(dir: File) {
|
|||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
cleanFiles(file)
|
cleanFiles(file)
|
||||||
} else if (file.isFile) {
|
} else if (file.isFile) {
|
||||||
if (file.path.toString().substringAfterLast(".") != "mp3") {
|
val filePath = file.path.toString()
|
||||||
Log.d("Files Cleaning", "Cleaning ${file.path}")
|
if (filePath.substringAfterLast(".") != "mp3" || filePath.isTempFile()) {
|
||||||
|
Log.d("Files Cleaning", "Cleaning $filePath")
|
||||||
file.delete()
|
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 |
@ -44,9 +44,19 @@ allprojects {
|
|||||||
freeCompilerArgs = (freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn"))
|
freeCompilerArgs = (freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy {
|
||||||
|
eachDependency {
|
||||||
|
if (requested.group == "org.jetbrains.kotlin") {
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
useVersion(deps.kotlin.kotlinGradlePlugin.get().versionConstraint.requiredVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()?.let { kmpExt ->
|
project.extensions.findByType<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension>()
|
||||||
|
?.let { kmpExt ->
|
||||||
kmpExt.sourceSets.run {
|
kmpExt.sourceSets.run {
|
||||||
all {
|
all {
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
|
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
|
||||||
|
@ -30,17 +30,20 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Androidx.gradlePlugin)
|
with(deps) {
|
||||||
implementation(JetBrains.Compose.gradlePlugin)
|
implementation(androidx.gradle.plugin)
|
||||||
implementation(JetBrains.Kotlin.gradlePlugin)
|
implementation(kotlin.compose.gradle)
|
||||||
implementation(JetBrains.Kotlin.serialization)
|
implementation(ktlint.gradle)
|
||||||
implementation(SqlDelight.gradlePlugin)
|
implementation(mosaic.gradle)
|
||||||
implementation(KTLint.gradlePlugin)
|
implementation(kotlin.kotlinGradlePlugin)
|
||||||
implementation(Internationalization.gradlePlugin)
|
implementation(sqldelight.gradle.plugin)
|
||||||
implementation(Mosaic.gradlePlugin)
|
implementation(i18n4k.gradle.plugin)
|
||||||
|
implementation(kotlin.serialization)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
// Add Deps to compilation, so it will become available in main project
|
// Add Deps to compilation, so it will become available in main project
|
||||||
sourceSets.getByName("main").kotlin.srcDir("buildSrc/src/main/kotlin")
|
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/>.
|
* * 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.ExternalModuleDependency
|
||||||
|
import org.gradle.api.artifacts.VersionCatalog
|
||||||
|
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
|
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
|
||||||
|
import org.gradle.kotlin.dsl.getByType
|
||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
// App's Version (To be bumped at each update)
|
// App's Version (To be bumped at each update)
|
||||||
@ -26,44 +30,10 @@ object Versions {
|
|||||||
|
|
||||||
const val versionCode = 26
|
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
|
// Android
|
||||||
const val minSdkVersion = 21
|
const val minSdkVersion = 21
|
||||||
const val compileSdkVersion = 30
|
const val compileSdkVersion = 31
|
||||||
const val targetSdkVersion = 29
|
const val targetSdkVersion = 29
|
||||||
const val androidxLifecycle = "2.4.0-alpha03"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object HostOS {
|
object HostOS {
|
||||||
@ -74,143 +44,46 @@ object HostOS {
|
|||||||
val isLinux = hostOs.startsWith("Linux", true)
|
val isLinux = hostOs.startsWith("Linux", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
object MultiPlatformSettings {
|
val Project.Deps: VersionCatalog get() = project.extensions.getByType<VersionCatalogsExtension>().named("deps")
|
||||||
const val dep = "com.russhwolf:multiplatform-settings-no-arg:0.7.7"
|
|
||||||
}
|
|
||||||
|
|
||||||
object KotlinJSWrappers {
|
val VersionCatalog.ktorBundle get() = findBundle("ktor").get()
|
||||||
private const val bomVersion = "0.0.1-pre.235-kotlin-1.5.21"
|
val VersionCatalog.statelyBundle get() = findBundle("stately").get()
|
||||||
val bom = "org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom:${bomVersion}"
|
val VersionCatalog.androidXLifecycleBundle get() = findBundle("androidx-lifecycle").get()
|
||||||
const val kotlinReact = "org.jetbrains.kotlin-wrappers:kotlin-react"
|
val VersionCatalog.androidXCommonBundle get() = findBundle("androidx-common").get()
|
||||||
const val kotlinReactDom = "org.jetbrains.kotlin-wrappers:kotlin-react-dom"
|
val VersionCatalog.kotlinTestBundle get() = findBundle("kotlin-test").get()
|
||||||
const val kotlinStyled = "org.jetbrains.kotlin-wrappers:kotlin-styled"
|
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 VersionCatalog.kotlinJunitTest get() = findDependency("kotlin-kotlinTestJunit").get()
|
||||||
val core = "io.insert-koin:koin-core:${Versions.koin}"
|
val VersionCatalog.kotlinJSTest get() = findDependency("kotlin-kotlinTestJs").get()
|
||||||
val test = "io.insert-koin:koin-test:${Versions.koin}"
|
val VersionCatalog.kermit get() = findDependency("kermit").get()
|
||||||
val android = "io.insert-koin:koin-android:${Versions.koin}"
|
val VersionCatalog.decompose get() = findDependency("decompose-dep").get()
|
||||||
val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}"
|
val VersionCatalog.decomposeComposeExt get() = findDependency("decompose-extensions-compose").get()
|
||||||
}
|
val VersionCatalog.jaffree get() = findDependency("jaffree").get()
|
||||||
|
|
||||||
object Androidx {
|
val VersionCatalog.ktlintGradle get() = findDependency("ktlint-gradle").get()
|
||||||
const val androidxActivity = "androidx.activity:activity-compose:1.3.1"
|
val VersionCatalog.androidGradle get() = findDependency("androidx-gradle-plugin").get()
|
||||||
const val core = "androidx.core:core-ktx:1.6.0"
|
val VersionCatalog.mosaicGradle get() = findDependency("mosaic-gradle").get()
|
||||||
const val palette = "androidx.palette:palette-ktx:1.0.0"
|
val VersionCatalog.kotlinComposeGradle get() = findDependency("kotlin-compose-gradle").get()
|
||||||
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutinesVersion}"
|
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"
|
val VersionCatalog.koinCore get() = findDependency("koin-core").get()
|
||||||
const val expresso = "androidx.test.espresso:espresso-core:3.3.0"
|
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"
|
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()
|
||||||
object KTLint {
|
val VersionCatalog.sqlDelightDriver get() = findDependency("sqldelight-driver").get()
|
||||||
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() }
|
|
||||||
|
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 {
|
sourceSets {
|
||||||
all {
|
all {
|
||||||
languageSettings.apply {
|
languageSettings.apply {
|
||||||
useExperimentalAnnotation("kotlin.RequiresOptIn")
|
optIn("kotlin.RequiresOptIn")
|
||||||
useExperimentalAnnotation("kotlin.Experimental")
|
optIn("kotlin.Experimental")
|
||||||
useExperimentalAnnotation("kotlin.time.ExperimentalTime")
|
optIn("kotlin.time.ExperimentalTime")
|
||||||
useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
|
optIn("kotlinx.serialization.ExperimentalSerializationApi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,36 +29,23 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
all {
|
all {
|
||||||
languageSettings.apply {
|
languageSettings.apply {
|
||||||
useExperimentalAnnotation("androidx.compose.animation")
|
optIn("androidx.compose.animation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("commonMain") {
|
named("commonMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
// Decompose
|
|
||||||
implementation(Decompose.decompose)
|
|
||||||
|
|
||||||
// MVI
|
|
||||||
implementation(MVIKotlin.coroutines)
|
|
||||||
implementation(MVIKotlin.mvikotlin)
|
|
||||||
|
|
||||||
implementation(compose.ui)
|
implementation(compose.ui)
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
|
implementation(Deps.kotlinCoroutines)
|
||||||
implementation(Extras.kermit)
|
implementation(Deps.decompose)
|
||||||
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
|
|
||||||
implementation(JetBrains.Kotlin.coroutines) {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
isForce = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("androidMain") {
|
named("androidMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Androidx.androidxActivity)
|
implementation(Deps.androidXCommonBundle)
|
||||||
implementation(Androidx.core)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("desktopMain") {
|
named("desktopMain") {
|
||||||
|
@ -42,23 +42,24 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
named("commonTest") {
|
named("commonTest") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(JetBrains.Kotlin.testCommon)
|
implementation(Deps.kotlinTestBundle)
|
||||||
implementation(JetBrains.Kotlin.testAnnotationsCommon)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
named("androidTest") {
|
named("androidTest") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(JetBrains.Kotlin.testJunit)
|
implementation(Deps.kotlinJunitTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("desktopTest") {
|
named("desktopTest") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(JetBrains.Kotlin.testJunit)
|
implementation(Deps.kotlinJunitTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("jsTest") {
|
named("jsTest") {
|
||||||
dependencies {}
|
dependencies {
|
||||||
|
implementation(Deps.kotlinJSTest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,45 +50,25 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
named("commonMain") {
|
named("commonMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
// Decompose
|
implementation(Deps.ktorBundle)
|
||||||
implementation(Decompose.decompose)
|
implementation(Deps.kotlinxSerialization)
|
||||||
|
implementation(Deps.kotlinCoroutines)
|
||||||
// MVI
|
implementation(Deps.mviKotlinBundle)
|
||||||
implementation(MVIKotlin.coroutines)
|
implementation(Deps.decompose)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(Deps.koinCore)
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
named("androidMain") {
|
named("androidMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Androidx.androidxActivity)
|
|
||||||
implementation(Androidx.core)
|
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Deps.androidXCommonBundle)
|
||||||
implementation(Ktor.clientAndroid)
|
implementation(Deps.decomposeComposeExt)
|
||||||
implementation(Koin.android)
|
implementation(Deps.ktorClientAndroid)
|
||||||
|
implementation(Deps.koinAndroidBundle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,27 +79,20 @@ kotlin {
|
|||||||
implementation(compose.material)
|
implementation(compose.material)
|
||||||
implementation(compose.desktop.common)
|
implementation(compose.desktop.common)
|
||||||
implementation(compose.materialIconsExtended)
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Deps.decomposeComposeExt)
|
||||||
implementation(Ktor.clientApache)
|
implementation(Deps.ktorClientApache)
|
||||||
implementation(Ktor.slf4j)
|
implementation(Deps.slf4j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("jsMain") {
|
named("jsMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Ktor.clientJs)
|
implementation(Deps.ktorClientJS)
|
||||||
|
|
||||||
/*with(KotlinJSWrappers) {
|
|
||||||
implementation(enforcedPlatform(bom))
|
|
||||||
implementation(kotlinReact)
|
|
||||||
implementation(kotlinReactDom)
|
|
||||||
implementation(kotlinStyled)
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HostOS.isMac) {
|
if (HostOS.isMac) {
|
||||||
named("iosMain") {
|
named("iosMain") {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Ktor.clientIos)
|
implementation(Deps.ktorClientIOS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:dependency-injection"))
|
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
|
@Composable
|
||||||
actual fun SaavnLogo() = getCachedPainter(R.drawable.ic_jio_saavn_logo)
|
actual fun SaavnLogo() = getCachedPainter(R.drawable.ic_jio_saavn_logo)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun SoundCloudLogo() = getCachedPainter(R.drawable.ic_soundcloud)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun GaanaLogo() = getCachedPainter(R.drawable.ic_gaana)
|
actual fun GaanaLogo() = getCachedPainter(R.drawable.ic_gaana)
|
||||||
|
|
||||||
|
@ -58,6 +58,9 @@ expect fun SpotifyLogo(): Painter
|
|||||||
@Composable
|
@Composable
|
||||||
expect fun SaavnLogo(): Painter
|
expect fun SaavnLogo(): Painter
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
expect fun SoundCloudLogo(): Painter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
expect fun YoutubeLogo(): Painter
|
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.main.SpotiFlyerMain.HomeCategory
|
||||||
import com.shabinder.common.models.DownloadRecord
|
import com.shabinder.common.models.DownloadRecord
|
||||||
import com.shabinder.common.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
|
import com.shabinder.common.models.spotify.Source
|
||||||
import com.shabinder.common.translations.Strings
|
import com.shabinder.common.translations.Strings
|
||||||
import com.shabinder.common.uikit.GaanaLogo
|
import com.shabinder.common.uikit.GaanaLogo
|
||||||
import com.shabinder.common.uikit.GithubLogo
|
import com.shabinder.common.uikit.GithubLogo
|
||||||
import com.shabinder.common.uikit.ImageLoad
|
import com.shabinder.common.uikit.ImageLoad
|
||||||
import com.shabinder.common.uikit.SaavnLogo
|
import com.shabinder.common.uikit.SaavnLogo
|
||||||
import com.shabinder.common.uikit.ShareImage
|
import com.shabinder.common.uikit.ShareImage
|
||||||
|
import com.shabinder.common.uikit.SoundCloudLogo
|
||||||
import com.shabinder.common.uikit.SpotifyLogo
|
import com.shabinder.common.uikit.SpotifyLogo
|
||||||
import com.shabinder.common.uikit.VerticalScrollbar
|
import com.shabinder.common.uikit.VerticalScrollbar
|
||||||
import com.shabinder.common.uikit.YoutubeLogo
|
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))
|
Spacer(modifier = Modifier.padding(top = 8.dp))
|
||||||
|
@ -21,15 +21,19 @@ package com.shabinder.common.uikit
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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.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.loadXmlImageVector
|
||||||
import androidx.compose.ui.res.vectorXmlResource
|
import androidx.compose.ui.res.useResource
|
||||||
|
import org.xml.sax.InputSource
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal actual fun <T> imageVectorResource(id: T): ImageVector =
|
internal actual fun <T> imageVectorResource(id: T): ImageVector {
|
||||||
vectorXmlResource(id as String)
|
val density = LocalDensity.current
|
||||||
|
return useResource(id as String) {
|
||||||
|
loadXmlImageVector(InputSource(it), density)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun DownloadImageTick() {
|
actual fun DownloadImageTick() {
|
||||||
@ -82,6 +86,10 @@ actual fun SpotifyLogo() =
|
|||||||
actual fun SaavnLogo() =
|
actual fun SaavnLogo() =
|
||||||
getCachedPainter("drawable/ic_jio_saavn_logo.xml")
|
getCachedPainter("drawable/ic_jio_saavn_logo.xml")
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun SoundCloudLogo() =
|
||||||
|
getCachedPainter("drawable/ic_soundcloud.xml")
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun YoutubeLogo() =
|
actual fun YoutubeLogo() =
|
||||||
getCachedPainter("drawable/ic_youtube.xml")
|
getCachedPainter("drawable/ic_youtube.xml")
|
||||||
|
@ -12,7 +12,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
|
|
||||||
actual val MARGIN_SCROLLBAR: Dp = 8.dp
|
actual val MARGIN_SCROLLBAR: Dp = 8.dp
|
||||||
|
|
||||||
actual typealias ScrollbarAdapter = androidx.compose.foundation.ScrollbarAdapter
|
actual typealias ScrollbarAdapter = ScrollbarAdapter
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -23,8 +23,6 @@ actual fun rememberScrollbarAdapter(
|
|||||||
): ScrollbarAdapter =
|
): ScrollbarAdapter =
|
||||||
androidx.compose.foundation.rememberScrollbarAdapter(
|
androidx.compose.foundation.rememberScrollbarAdapter(
|
||||||
scrollState = scrollState,
|
scrollState = scrollState,
|
||||||
itemCount = itemCount,
|
|
||||||
averageItemSize = averageItemSize
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -10,29 +10,37 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
api("org.jetbrains.kotlinx:atomicfu:0.16.2")
|
with(deps) {
|
||||||
api(MultiPlatformSettings.dep)
|
api(multiplatform.settings)
|
||||||
implementation(MVIKotlin.rx)
|
api(kotlinx.atomicfu)
|
||||||
|
implementation(mviKotlin.rx)
|
||||||
|
implementation(decompose.dep)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Extras.mp3agic)
|
with(deps) {
|
||||||
implementation(Extras.Android.countly)
|
implementation(mp3agic)
|
||||||
|
implementation(countly.android)
|
||||||
|
}
|
||||||
implementation(project(":ffmpeg:android-ffmpeg"))
|
implementation(project(":ffmpeg:android-ffmpeg"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
desktopMain {
|
desktopMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Extras.mp3agic)
|
with(deps) {
|
||||||
implementation(Extras.Desktop.countly)
|
implementation(mp3agic)
|
||||||
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
|
implementation(countly.desktop)
|
||||||
|
implementation(jaffree)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(npm("browser-id3-writer", "4.4.0"))
|
implementation(npm("browser-id3-writer", "4.4.0"))
|
||||||
implementation(npm("file-saver", "2.0.4"))
|
implementation(npm("file-saver", "2.0.4"))
|
||||||
|
implementation(deps.kotlin.js.wrappers.ext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ class AndroidFileManager(
|
|||||||
|
|
||||||
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture =
|
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture =
|
||||||
withContext(dispatcherIO) {
|
withContext(dispatcherIO) {
|
||||||
val cachePath = imageCacheDir() + getNameURL(url)
|
val cachePath = getImageCachePath(url)
|
||||||
Picture(
|
Picture(
|
||||||
image = (loadCachedImage(cachePath, reqWidth, reqHeight) ?: freshImage(
|
image = (loadCachedImage(cachePath, reqWidth, reqHeight) ?: freshImage(
|
||||||
url,
|
url,
|
||||||
@ -214,7 +214,7 @@ class AndroidFileManager(
|
|||||||
// Decode and Cache Full Sized Image in Background
|
// Decode and Cache Full Sized Image in Background
|
||||||
cacheImage(
|
cacheImage(
|
||||||
BitmapFactory.decodeByteArray(input, 0, input.size),
|
BitmapFactory.decodeByteArray(input, 0, input.size),
|
||||||
imageCacheDir() + getNameURL(url)
|
getImageCachePath(url)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
bitmap // return Memory Efficient Bitmap
|
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.TrackDetails
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
import com.shabinder.common.utils.removeIllegalChars
|
import com.shabinder.common.utils.removeIllegalChars
|
||||||
|
import com.shabinder.common.utils.requireNotNull
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.http.*
|
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.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
@ -80,12 +84,13 @@ fun FileManager.createDirectories() {
|
|||||||
if (!defaultDir().contains("null${fileSeparator()}SpotiFlyer")) {
|
if (!defaultDir().contains("null${fileSeparator()}SpotiFlyer")) {
|
||||||
createDirectory(defaultDir())
|
createDirectory(defaultDir())
|
||||||
createDirectory(imageCacheDir())
|
createDirectory(imageCacheDir())
|
||||||
createDirectory(defaultDir() + "Tracks/")
|
createDirectory(defaultDir() + "Tracks" + fileSeparator())
|
||||||
createDirectory(defaultDir() + "Albums/")
|
createDirectory(defaultDir() + "Albums" + fileSeparator())
|
||||||
createDirectory(defaultDir() + "Playlists/")
|
createDirectory(defaultDir() + "Playlists" + fileSeparator())
|
||||||
createDirectory(defaultDir() + "YT_Downloads/")
|
createDirectory(defaultDir() + "YT_Downloads" + fileSeparator())
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
}
|
}
|
||||||
} catch (ignored: Exception) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FileManager.finalOutputDir(
|
fun FileManager.finalOutputDir(
|
||||||
@ -100,24 +105,50 @@ fun FileManager.finalOutputDir(
|
|||||||
removeIllegalChars(subFolder) + this.fileSeparator()
|
removeIllegalChars(subFolder) + this.fileSeparator()
|
||||||
} +
|
} +
|
||||||
removeIllegalChars(itemName) + extension
|
removeIllegalChars(itemName) + extension
|
||||||
/*DIR Specific Operation End*/
|
|
||||||
|
|
||||||
fun getNameURL(url: String): String {
|
fun FileManager.getImageCachePath(
|
||||||
return url.substring(url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1, url.length)
|
url: String
|
||||||
.replace('/', '_')
|
): 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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun downloadFile(url: String): Flow<DownloadResult> {
|
return fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun HttpClient.downloadFile(url: String) = downloadFile(url, this)
|
||||||
|
|
||||||
|
suspend fun downloadFile(url: String, client: HttpClient? = null): Flow<DownloadResult> {
|
||||||
return flow {
|
return flow {
|
||||||
val client = createHttpClient()
|
val httpClient = client ?: createHttpClient()
|
||||||
val response = client.get<HttpStatement>(url).execute()
|
val response = httpClient.get<HttpStatement>(url).execute()
|
||||||
val data = ByteArray(response.contentLength()!!.toInt())
|
// Not all requests return Content Length
|
||||||
|
val data = kotlin.runCatching {
|
||||||
|
ByteArray(response.contentLength().requireNotNull().toInt())
|
||||||
|
}.getOrNull() ?: byteArrayOf()
|
||||||
var offset = 0
|
var offset = 0
|
||||||
do {
|
do {
|
||||||
// Set Length optimally, after how many kb you want a progress update, now it 0.25mb
|
// 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)
|
val currentRead = response.content.readAvailable(data, offset, 2_50_000)
|
||||||
offset += currentRead
|
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))
|
emit(DownloadResult.Progress(progress))
|
||||||
} while (currentRead > 0)
|
} while (currentRead > 0)
|
||||||
if (response.status.isSuccess()) {
|
if (response.status.isSuccess()) {
|
||||||
@ -125,7 +156,10 @@ suspend fun downloadFile(url: String): Flow<DownloadResult> {
|
|||||||
} else {
|
} else {
|
||||||
emit(DownloadResult.Error("File not downloaded"))
|
emit(DownloadResult.Error("File not downloaded"))
|
||||||
}
|
}
|
||||||
client.close()
|
|
||||||
|
// Close Client if We Created One
|
||||||
|
if (client == null)
|
||||||
|
httpClient.close()
|
||||||
}.catch { e ->
|
}.catch { e ->
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
emit(DownloadResult.Error(e.message ?: "File not downloaded"))
|
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.json.serializer.*
|
||||||
import io.ktor.client.features.logging.*
|
import io.ktor.client.features.logging.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.native.concurrent.SharedImmutable
|
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 {
|
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
|
||||||
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
|
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
|
||||||
install(JsonFeature) {
|
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.setId3v1Tags
|
||||||
import com.shabinder.common.core_components.setId3v2TagsAndSaveFile
|
import com.shabinder.common.core_components.setId3v2TagsAndSaveFile
|
||||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||||
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.dispatcherIO
|
import com.shabinder.common.models.dispatcherIO
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
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.event.coroutines.map
|
||||||
import com.shabinder.common.models.Actions
|
|
||||||
import com.shabinder.database.Database
|
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 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.bind
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
@ -104,8 +107,10 @@ class DesktopFileManager(
|
|||||||
|
|
||||||
override suspend fun cacheImage(image: Any, path: String): Unit = withContext(dispatcherIO) {
|
override suspend fun cacheImage(image: Any, path: String): Unit = withContext(dispatcherIO) {
|
||||||
try {
|
try {
|
||||||
|
val file = File(path)
|
||||||
|
if(!file.parentFile.exists()) createDirectories()
|
||||||
(image as? BufferedImage)?.let {
|
(image as? BufferedImage)?.let {
|
||||||
ImageIO.write(it, "jpeg", File(path))
|
ImageIO.write(it, "jpeg", file)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@ -175,8 +180,7 @@ class DesktopFileManager(
|
|||||||
override fun addToLibrary(path: String) {}
|
override fun addToLibrary(path: String) {}
|
||||||
|
|
||||||
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture {
|
override suspend fun loadImage(url: String, reqWidth: Int, reqHeight: Int): Picture {
|
||||||
val cachePath = imageCacheDir() + getNameURL(url)
|
var picture: ImageBitmap? = loadCachedImage(getImageCachePath(url), reqWidth, reqHeight)
|
||||||
var picture: ImageBitmap? = loadCachedImage(cachePath, reqWidth, reqHeight)
|
|
||||||
if (picture == null) picture = freshImage(url, reqWidth, reqHeight)
|
if (picture == null) picture = freshImage(url, reqWidth, reqHeight)
|
||||||
return Picture(image = picture)
|
return Picture(image = picture)
|
||||||
}
|
}
|
||||||
@ -205,7 +209,7 @@ class DesktopFileManager(
|
|||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
GlobalScope.launch(Dispatchers.IO) { // TODO Refactor
|
GlobalScope.launch(Dispatchers.IO) { // TODO Refactor
|
||||||
cacheImage(result, imageCacheDir() + getNameURL(url))
|
cacheImage(result, getImageCachePath(url))
|
||||||
}
|
}
|
||||||
result.toImageBitmap()
|
result.toImageBitmap()
|
||||||
} else null
|
} else null
|
||||||
|
@ -25,9 +25,6 @@ plugins {
|
|||||||
id("de.comahe.i18n4k")
|
id("de.comahe.i18n4k")
|
||||||
}
|
}
|
||||||
|
|
||||||
val statelyVersion = "1.1.7"
|
|
||||||
val statelyIsoVersion = "1.1.7-a1"
|
|
||||||
|
|
||||||
i18n4k {
|
i18n4k {
|
||||||
inputDirectory = "../../translations"
|
inputDirectory = "../../translations"
|
||||||
packageName = "com.shabinder.common.translations"
|
packageName = "com.shabinder.common.translations"
|
||||||
@ -50,11 +47,13 @@ kotlin {
|
|||||||
}
|
}
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("co.touchlab:stately-concurrency:$statelyVersion")
|
with(deps) {
|
||||||
implementation("co.touchlab:stately-isolate:$statelyIsoVersion")
|
api(bundles.stately)
|
||||||
implementation("co.touchlab:stately-iso-collections:$statelyIsoVersion")
|
api(i18n4k.core)
|
||||||
implementation(Extras.youtubeDownloader)
|
api(kermit)
|
||||||
api(Internationalization.dep)
|
api(moko.parcelize)
|
||||||
|
implementation(youtube.downloader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ data class TrackDetails(
|
|||||||
var audioQuality: AudioQuality = AudioQuality.KBPS192,
|
var audioQuality: AudioQuality = AudioQuality.KBPS192,
|
||||||
var audioFormat: AudioFormat = AudioFormat.MP4,
|
var audioFormat: AudioFormat = AudioFormat.MP4,
|
||||||
var outputFilePath: String, // UriString in Android
|
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 {
|
) : Parcelable {
|
||||||
val outputMp3Path get() = outputFilePath.substringBeforeLast(".") + ".mp3"
|
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"
|
override val message: String = /*${Strings.mp3ConverterBusy()} */"CAUSE:$extraInfo"
|
||||||
) : SpotiFlyerException(message)
|
) : 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(
|
data class UnknownReason(
|
||||||
val exception: Throwable? = null,
|
val exception: Throwable? = null,
|
||||||
override val message: String = Strings.unknownError()
|
override val message: String = Strings.unknownError()
|
||||||
|
@ -20,7 +20,7 @@ data class SaavnSong @OptIn(ExperimentalSerializationApi::class) constructor(
|
|||||||
// val explicit_content: Int = 0,
|
// val explicit_content: Int = 0,
|
||||||
val has_lyrics: Boolean = false,
|
val has_lyrics: Boolean = false,
|
||||||
val id: String,
|
val id: String,
|
||||||
val image: String,
|
val image: String = "",
|
||||||
val label: String? = null,
|
val label: String? = null,
|
||||||
val label_url: String? = null,
|
val label_url: String? = null,
|
||||||
val language: String,
|
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,
|
Spotify,
|
||||||
YouTube,
|
YouTube,
|
||||||
Gaana,
|
Gaana,
|
||||||
JioSaavn
|
JioSaavn,
|
||||||
|
SoundCloud
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ val globalJson by lazy {
|
|||||||
Json {
|
Json {
|
||||||
isLenient = true
|
isLenient = true
|
||||||
ignoreUnknownKeys = 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"))
|
implementation(project(":common:data-models"))
|
||||||
|
|
||||||
// SQL Delight
|
// SQL Delight
|
||||||
implementation(SqlDelight.runtime)
|
with(deps.sqldelight) {
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
implementation(runtime)
|
||||||
|
api(coroutines.extension)
|
||||||
// koin
|
}
|
||||||
implementation(Koin.test)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(SqlDelight.androidDriver)
|
implementation(deps.sqldelight.android.driver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
desktopMain {
|
desktopMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(SqlDelight.sqliteDriver)
|
with(deps) {
|
||||||
implementation(SqlDelight.jdbcDriver)
|
implementation(sqldelight.driver)
|
||||||
|
implementation(sqlite.jdbc.driver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HostOS.isMac) {
|
if (HostOS.isMac) {
|
||||||
val iosMain by getting {
|
val iosMain by getting {
|
||||||
dependencies {
|
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/>.
|
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.jetbrains.compose.compose
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
id("multiplatform-setup")
|
id("multiplatform-setup")
|
||||||
|
@ -30,7 +30,6 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
implementation(project(":common:core-components"))
|
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> {
|
private fun List<TrackDetails>.updateTracksStatuses(map: Map<String, DownloadStatus>): List<TrackDetails> {
|
||||||
val titleList = this.map { it.title }
|
val updatedList = ArrayList(this)
|
||||||
val updatedList = mutableListOf<TrackDetails>().also { it.addAll(this) }
|
LinkedHashMap(map).forEach { newTrack ->
|
||||||
|
indexOfFirst { it.title == newTrack.key }.let { position ->
|
||||||
for (newTrack in map) {
|
|
||||||
titleList.indexOf(newTrack.key).let { position ->
|
|
||||||
if (position != -1) {
|
if (position != -1) {
|
||||||
updatedList.getOrNull(position)?.copy(
|
updatedList.getOrNull(position)?.copy(
|
||||||
downloaded = newTrack.value,
|
downloaded = newTrack.value,
|
||||||
|
@ -30,7 +30,6 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
implementation(project(":common:core-components"))
|
implementation(project(":common:core-components"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:core-components"))
|
implementation(project(":common:core-components"))
|
||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,23 +12,25 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
with(deps) {
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:core-components"))
|
implementation(project(":common:core-components"))
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.2.1")
|
implementation(youtube.downloader)
|
||||||
implementation(Extras.youtubeDownloader)
|
implementation(fuzzy.wuzzy)
|
||||||
implementation(Extras.fuzzyWuzzy)
|
implementation(kotlinx.datetime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
androidMain {
|
androidMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Extras.mp3agic)
|
implementation(deps.mp3agic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
desktopMain {
|
desktopMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Extras.mp3agic)
|
implementation(deps.mp3agic)
|
||||||
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
|
implementation(deps.jaffree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsMain {
|
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.file_manager.FileManager
|
||||||
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
import com.shabinder.common.core_components.preference_manager.PreferenceManager
|
||||||
import com.shabinder.common.database.DownloadRecordDatabaseQueries
|
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.SuspendableEvent
|
||||||
import com.shabinder.common.models.event.coroutines.flatMapError
|
import com.shabinder.common.models.event.coroutines.flatMapError
|
||||||
import com.shabinder.common.models.event.coroutines.onSuccess
|
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.models.spotify.Source
|
||||||
import com.shabinder.common.providers.gaana.GaanaProvider
|
import com.shabinder.common.providers.gaana.GaanaProvider
|
||||||
import com.shabinder.common.providers.saavn.SaavnProvider
|
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.SpotifyProvider
|
||||||
import com.shabinder.common.providers.youtube.YoutubeProvider
|
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_music.YoutubeMusic
|
||||||
import com.shabinder.common.providers.youtube_to_mp3.requests.YoutubeMp3
|
import com.shabinder.common.providers.youtube_to_mp3.requests.YoutubeMp3
|
||||||
import com.shabinder.common.utils.appendPadded
|
import com.shabinder.common.utils.appendPadded
|
||||||
@ -45,6 +50,7 @@ class FetchPlatformQueryResult(
|
|||||||
private val spotifyProvider: SpotifyProvider,
|
private val spotifyProvider: SpotifyProvider,
|
||||||
private val youtubeProvider: YoutubeProvider,
|
private val youtubeProvider: YoutubeProvider,
|
||||||
private val saavnProvider: SaavnProvider,
|
private val saavnProvider: SaavnProvider,
|
||||||
|
private val soundCloudProvider: SoundCloudProvider,
|
||||||
private val youtubeMusic: YoutubeMusic,
|
private val youtubeMusic: YoutubeMusic,
|
||||||
private val youtubeMp3: YoutubeMp3,
|
private val youtubeMp3: YoutubeMp3,
|
||||||
val fileManager: FileManager,
|
val fileManager: FileManager,
|
||||||
@ -74,6 +80,10 @@ class FetchPlatformQueryResult(
|
|||||||
link.contains("gaana", true) ->
|
link.contains("gaana", true) ->
|
||||||
gaanaProvider.query(link)
|
gaanaProvider.query(link)
|
||||||
|
|
||||||
|
// SoundCloud
|
||||||
|
link.contains("soundcloud", true) ->
|
||||||
|
soundCloudProvider.query(link)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
SuspendableEvent.error(SpotiFlyerException.LinkInvalid(link))
|
SuspendableEvent.error(SpotiFlyerException.LinkInvalid(link))
|
||||||
}
|
}
|
||||||
@ -122,7 +132,7 @@ class FetchPlatformQueryResult(
|
|||||||
ytMp3Link.component2()?.stackTraceToString()
|
ytMp3Link.component2()?.stackTraceToString()
|
||||||
?: "couldn't fetch link for ${track.videoID} ,trying manual extraction"
|
?: "couldn't fetch link for ${track.videoID} ,trying manual extraction"
|
||||||
)
|
)
|
||||||
appendLine("Trying Local Extraction")
|
//appendLine("Trying Local Extraction")
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
audioFormat = AudioFormat.MP3
|
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 -> {
|
else -> {
|
||||||
appendPadded(
|
appendPadded(
|
||||||
"Invalid Arguments",
|
"Invalid Arguments",
|
||||||
|
@ -2,6 +2,7 @@ package com.shabinder.common.providers
|
|||||||
|
|
||||||
import com.shabinder.common.providers.gaana.GaanaProvider
|
import com.shabinder.common.providers.gaana.GaanaProvider
|
||||||
import com.shabinder.common.providers.saavn.SaavnProvider
|
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.SpotifyProvider
|
||||||
import com.shabinder.common.providers.spotify.token_store.TokenStore
|
import com.shabinder.common.providers.spotify.token_store.TokenStore
|
||||||
import com.shabinder.common.providers.youtube.YoutubeProvider
|
import com.shabinder.common.providers.youtube.YoutubeProvider
|
||||||
@ -16,7 +17,8 @@ fun providersModule(enableNetworkLogs: Boolean) = module {
|
|||||||
single { GaanaProvider(get(), get(), get()) }
|
single { GaanaProvider(get(), get(), get()) }
|
||||||
single { SaavnProvider(get(), get(), get()) }
|
single { SaavnProvider(get(), get(), get()) }
|
||||||
single { YoutubeProvider(get(), get(), get()) }
|
single { YoutubeProvider(get(), get(), get()) }
|
||||||
|
single { SoundCloudProvider(get(), get(), get()) }
|
||||||
single { YoutubeMp3(get(), get()) }
|
single { YoutubeMp3(get(), get()) }
|
||||||
single { YoutubeMusic(get(), get(), get(), 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 co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
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.finalOutputDir
|
||||||
|
import com.shabinder.common.core_components.file_manager.getImageCachePath
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.SpotiFlyerException
|
import com.shabinder.common.models.SpotiFlyerException
|
||||||
@ -124,8 +125,7 @@ class GaanaProvider(
|
|||||||
title = it.track_title,
|
title = it.track_title,
|
||||||
artists = it.artist.map { artist -> artist?.name.toString() },
|
artists = it.artist.map { artist -> artist?.name.toString() },
|
||||||
durationSec = it.duration,
|
durationSec = it.duration,
|
||||||
albumArtPath = fileManager.imageCacheDir() + (it.artworkLink.substringBeforeLast('/')
|
albumArtPath = fileManager.getImageCachePath(it.artworkLink),
|
||||||
.substringAfterLast('/')) + ".jpeg",
|
|
||||||
albumName = it.album_title,
|
albumName = it.album_title,
|
||||||
genre = it.genre?.mapNotNull { genre -> genre?.name } ?: emptyList(),
|
genre = it.genre?.mapNotNull { genre -> genre?.name } ?: emptyList(),
|
||||||
year = it.release_date,
|
year = it.release_date,
|
||||||
|
@ -25,10 +25,12 @@ import com.shabinder.common.models.gaana.GaanaSong
|
|||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
|
||||||
|
interface GaanaRequests {
|
||||||
|
|
||||||
|
companion object {
|
||||||
private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990"
|
private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990"
|
||||||
private val BASE_URL get() = "${corsApi}https://api.gaana.com"
|
private val BASE_URL get() = "${corsApi}https://api.gaana.com"
|
||||||
|
}
|
||||||
interface GaanaRequests {
|
|
||||||
|
|
||||||
val httpClient: HttpClient
|
val httpClient: HttpClient
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.shabinder.common.providers.saavn
|
|||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
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.finalOutputDir
|
||||||
|
import com.shabinder.common.core_components.file_manager.getImageCachePath
|
||||||
import com.shabinder.common.models.*
|
import com.shabinder.common.models.*
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
import com.shabinder.common.models.saavn.SaavnSong
|
import com.shabinder.common.models.saavn.SaavnSong
|
||||||
@ -28,7 +29,7 @@ class SaavnProvider(
|
|||||||
).apply {
|
).apply {
|
||||||
val pageLink = fullLink.substringAfter("saavn.com/").substringBefore("?")
|
val pageLink = fullLink.substringAfter("saavn.com/").substringBefore("?")
|
||||||
when {
|
when {
|
||||||
pageLink.contains("/song/", true) -> {
|
pageLink.contains("song/", true) -> {
|
||||||
getSong(fullLink).value.let {
|
getSong(fullLink).value.let {
|
||||||
folderType = "Tracks"
|
folderType = "Tracks"
|
||||||
subFolder = ""
|
subFolder = ""
|
||||||
@ -37,7 +38,7 @@ class SaavnProvider(
|
|||||||
coverUrl = it.image.replace("http:", "https:")
|
coverUrl = it.image.replace("http:", "https:")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageLink.contains("/album/", true) -> {
|
pageLink.contains("album/", true) -> {
|
||||||
getAlbum(fullLink).value.let {
|
getAlbum(fullLink).value.let {
|
||||||
folderType = "Albums"
|
folderType = "Albums"
|
||||||
subFolder = removeIllegalChars(it.title)
|
subFolder = removeIllegalChars(it.title)
|
||||||
@ -46,7 +47,7 @@ class SaavnProvider(
|
|||||||
coverUrl = it.image.replace("http:", "https:")
|
coverUrl = it.image.replace("http:", "https:")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageLink.contains("/featured/", true) -> { // Playlist
|
pageLink.contains("featured/", true) -> { // Playlist
|
||||||
getPlaylist(fullLink).value.let {
|
getPlaylist(fullLink).value.let {
|
||||||
folderType = "Playlists"
|
folderType = "Playlists"
|
||||||
subFolder = removeIllegalChars(it.listname)
|
subFolder = removeIllegalChars(it.listname)
|
||||||
@ -68,7 +69,7 @@ class SaavnProvider(
|
|||||||
artists = it.artistMap.keys.toMutableSet().apply { addAll(it.singers.split(",")) }.toList(),
|
artists = it.artistMap.keys.toMutableSet().apply { addAll(it.singers.split(",")) }.toList(),
|
||||||
durationSec = it.duration.toInt(),
|
durationSec = it.duration.toInt(),
|
||||||
albumName = it.album,
|
albumName = it.album,
|
||||||
albumArtPath = fileManager.imageCacheDir() + (it.image.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg",
|
albumArtPath = fileManager.getImageCachePath(it.image),
|
||||||
year = it.year,
|
year = it.year,
|
||||||
comment = it.copyright_text,
|
comment = it.copyright_text,
|
||||||
trackUrl = it.perma_url,
|
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.getJsonArray
|
||||||
import io.github.shabinder.utils.getJsonObject
|
import io.github.shabinder.utils.getJsonObject
|
||||||
import io.github.shabinder.utils.getString
|
import io.github.shabinder.utils.getString
|
||||||
import io.ktor.client.*
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.get
|
||||||
import kotlinx.serialization.json.*
|
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
|
import kotlin.collections.set
|
||||||
|
|
||||||
interface JioSaavnRequests {
|
interface JioSaavnRequests {
|
||||||
@ -33,8 +40,8 @@ interface JioSaavnRequests {
|
|||||||
trackArtists: List<String>,
|
trackArtists: List<String>,
|
||||||
preferredQuality: AudioQuality
|
preferredQuality: AudioQuality
|
||||||
): SuspendableEvent<Pair<String, AudioQuality>, Throwable> = searchForSong(trackName).map { songs ->
|
): SuspendableEvent<Pair<String, AudioQuality>, Throwable> = searchForSong(trackName).map { songs ->
|
||||||
val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull() ?:
|
val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull()
|
||||||
throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName")
|
?: throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName")
|
||||||
|
|
||||||
var audioQuality: AudioQuality = AudioQuality.KBPS160
|
var audioQuality: AudioQuality = AudioQuality.KBPS160
|
||||||
val m4aLink: String by getSongFromID(bestMatch).map { song ->
|
val m4aLink: String by getSongFromID(bestMatch).map { song ->
|
||||||
@ -235,8 +242,8 @@ interface JioSaavnRequests {
|
|||||||
for (result in tracks) {
|
for (result in tracks) {
|
||||||
var hasCommonWord = false
|
var hasCommonWord = false
|
||||||
|
|
||||||
val resultName = result.title.lowercase().replace("/", " ")
|
val resultName = result.title.toLowerCase().replace("/", " ")
|
||||||
val trackNameWords = trackName.lowercase().split(" ")
|
val trackNameWords = trackName.toLowerCase().split(" ")
|
||||||
|
|
||||||
for (nameWord in trackNameWords) {
|
for (nameWord in trackNameWords) {
|
||||||
if (nameWord.isNotBlank() && FuzzySearch.partialRatio(nameWord, resultName) > 85) hasCommonWord = true
|
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
|
// String Containing All Artist Names from JioSaavn Search Result
|
||||||
val artistListString = mutableSetOf<String>().apply {
|
val artistListString = mutableSetOf<String>().apply {
|
||||||
result.more_info?.singers?.split(",")?.let { addAll(it) }
|
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(" , ")
|
}.joinToString(" , ")
|
||||||
|
|
||||||
for (artist in trackArtists) {
|
for (artist in trackArtists) {
|
||||||
if (FuzzySearch.partialRatio(artist.lowercase(), artistListString) > 85)
|
if (FuzzySearch.partialRatio(artist.toLowerCase(), artistListString) > 85)
|
||||||
artistMatchNumber++
|
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 co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
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.finalOutputDir
|
||||||
|
import com.shabinder.common.core_components.file_manager.getImageCachePath
|
||||||
import com.shabinder.common.core_components.utils.createHttpClient
|
import com.shabinder.common.core_components.utils.createHttpClient
|
||||||
import com.shabinder.common.models.*
|
import com.shabinder.common.models.*
|
||||||
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
import com.shabinder.common.models.event.coroutines.SuspendableEvent
|
||||||
@ -79,7 +80,7 @@ class SpotifyProvider(
|
|||||||
|
|
||||||
if (type == "episode" || type == "show") {
|
if (type == "episode" || type == "show") {
|
||||||
throw SpotiFlyerException.FeatureNotImplementedYet(
|
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(),
|
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
|
||||||
albumArtists = it.album?.artists?.mapNotNull { artist -> artist?.name } ?: emptyList(),
|
albumArtists = it.album?.artists?.mapNotNull { artist -> artist?.name } ?: emptyList(),
|
||||||
durationSec = (it.duration_ms / 1000).toInt(),
|
durationSec = (it.duration_ms / 1000).toInt(),
|
||||||
albumArtPath = fileManager.imageCacheDir() + (it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast(
|
albumArtPath = fileManager.getImageCachePath(it.album?.images?.firstOrNull()?.url ?: ""),
|
||||||
'/'
|
|
||||||
) + ".jpeg",
|
|
||||||
albumName = it.album?.name,
|
albumName = it.album?.name,
|
||||||
year = it.album?.release_date,
|
year = it.album?.release_date,
|
||||||
comment = "Genres:${it.album?.genres?.joinToString()}",
|
comment = "Genres:${it.album?.genres?.joinToString()}",
|
||||||
|
@ -19,6 +19,7 @@ package com.shabinder.common.providers.youtube
|
|||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
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.finalOutputDir
|
||||||
|
import com.shabinder.common.core_components.file_manager.getImageCachePath
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.SpotiFlyerException
|
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.YoutubeVideo
|
||||||
import io.github.shabinder.models.formats.Format
|
import io.github.shabinder.models.formats.Format
|
||||||
import io.github.shabinder.models.quality.AudioQuality
|
import io.github.shabinder.models.quality.AudioQuality
|
||||||
import io.ktor.client.*
|
import io.ktor.client.HttpClient
|
||||||
|
|
||||||
class YoutubeProvider(
|
class YoutubeProvider(
|
||||||
private val httpClient: HttpClient,
|
private val httpClient: HttpClient,
|
||||||
@ -108,13 +109,14 @@ class YoutubeProvider(
|
|||||||
title = name
|
title = name
|
||||||
|
|
||||||
trackList = videos.map {
|
trackList = videos.map {
|
||||||
|
val imageURL = "https://i.ytimg.com/vi/${it.videoId}/hqdefault.jpg"
|
||||||
TrackDetails(
|
TrackDetails(
|
||||||
title = it.title ?: "N/A",
|
title = it.title ?: "N/A",
|
||||||
artists = listOf(it.author ?: "N/A"),
|
artists = listOf(it.author ?: "N/A"),
|
||||||
durationSec = it.lengthSeconds,
|
durationSec = it.lengthSeconds,
|
||||||
albumArtPath = fileManager.imageCacheDir() + it.videoId + ".jpeg",
|
albumArtPath = fileManager.getImageCachePath(imageURL),
|
||||||
source = Source.YouTube,
|
source = Source.YouTube,
|
||||||
albumArtURL = "https://i.ytimg.com/vi/${it.videoId}/hqdefault.jpg",
|
albumArtURL = imageURL,
|
||||||
downloaded = if (fileManager.isPresent(
|
downloaded = if (fileManager.isPresent(
|
||||||
fileManager.finalOutputDir(
|
fileManager.finalOutputDir(
|
||||||
itemName = it.title ?: "N/A",
|
itemName = it.title ?: "N/A",
|
||||||
@ -155,7 +157,7 @@ class YoutubeProvider(
|
|||||||
val video = ytDownloader.getVideo(searchId)
|
val video = ytDownloader.getVideo(searchId)
|
||||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||||
val detail = video.videoDetails
|
val detail = video.videoDetails
|
||||||
val name = detail.title?.replace(detail.author?.uppercase() ?: "", "", true)
|
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
|
||||||
?: detail.title ?: ""
|
?: detail.title ?: ""
|
||||||
// logger.i{ detail.toString() }
|
// logger.i{ detail.toString() }
|
||||||
trackList = listOf(
|
trackList = listOf(
|
||||||
@ -163,9 +165,9 @@ class YoutubeProvider(
|
|||||||
title = name,
|
title = name,
|
||||||
artists = listOf(detail.author ?: "N/A"),
|
artists = listOf(detail.author ?: "N/A"),
|
||||||
durationSec = detail.lengthSeconds,
|
durationSec = detail.lengthSeconds,
|
||||||
albumArtPath = fileManager.imageCacheDir() + "$searchId.jpeg",
|
albumArtPath = fileManager.getImageCachePath(coverUrl),
|
||||||
source = Source.YouTube,
|
source = Source.YouTube,
|
||||||
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
|
albumArtURL = coverUrl,
|
||||||
downloaded = if (fileManager.isPresent(
|
downloaded = if (fileManager.isPresent(
|
||||||
fileManager.finalOutputDir(
|
fileManager.finalOutputDir(
|
||||||
itemName = name,
|
itemName = name,
|
||||||
@ -179,7 +181,12 @@ class YoutubeProvider(
|
|||||||
else {
|
else {
|
||||||
DownloadStatus.NotDownloaded
|
DownloadStatus.NotDownloaded
|
||||||
},
|
},
|
||||||
outputFilePath = fileManager.finalOutputDir(name, folderType, subFolder, fileManager.defaultDir()/*,".m4a"*/),
|
outputFilePath = fileManager.finalOutputDir(
|
||||||
|
name,
|
||||||
|
folderType,
|
||||||
|
subFolder,
|
||||||
|
fileManager.defaultDir()/*,".m4a"*/
|
||||||
|
),
|
||||||
videoID = searchId
|
videoID = searchId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -18,19 +18,33 @@ package com.shabinder.common.providers.youtube_music
|
|||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.core_components.file_manager.FileManager
|
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.SuspendableEvent
|
||||||
import com.shabinder.common.models.event.coroutines.flatMap
|
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.models.event.coroutines.map
|
||||||
import com.shabinder.common.providers.youtube.YoutubeProvider
|
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 com.shabinder.common.providers.youtube_to_mp3.requests.YoutubeMp3
|
||||||
import io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
|
import io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
|
||||||
import io.ktor.client.*
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.headers
|
||||||
import io.ktor.http.*
|
import io.ktor.client.request.post
|
||||||
import kotlinx.serialization.json.*
|
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.collections.set
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@ -168,7 +182,7 @@ class YoutubeMusic constructor(
|
|||||||
! 4 - Duration (hh:mm:ss)
|
! 4 - Duration (hh:mm:ss)
|
||||||
!
|
!
|
||||||
! We blindly gather all the details we get our hands on, then
|
! 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
|
! we do so only if their Type is 'Song' or 'Video
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -180,7 +194,7 @@ class YoutubeMusic constructor(
|
|||||||
/*
|
/*
|
||||||
Filter Out dummies here itself
|
Filter Out dummies here itself
|
||||||
! 'musicResponsiveListItemFlexColumnRenderer' should have more that one
|
! '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.
|
! I have no clue. We skip these.
|
||||||
|
|
||||||
! Remember that we appended the linkBlock to result, treating that like the
|
! 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 (detailArray in result.subList(0, result.size - 1)) {
|
||||||
for (detail in detailArray.jsonArray) {
|
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
|
// if not a dummy, collect All Variables
|
||||||
val details =
|
val details =
|
||||||
@ -262,8 +276,8 @@ class YoutubeMusic constructor(
|
|||||||
// most song results on youtube go by $artist - $songName or artist1/artist2
|
// most song results on youtube go by $artist - $songName or artist1/artist2
|
||||||
var hasCommonWord = false
|
var hasCommonWord = false
|
||||||
|
|
||||||
val resultName = result.name?.lowercase()?.replace("-", " ")?.replace("/", " ") ?: ""
|
val resultName = result.name?.toLowerCase()?.replace("-", " ")?.replace("/", " ") ?: ""
|
||||||
val trackNameWords = trackName.lowercase().split(" ")
|
val trackNameWords = trackName.toLowerCase().split(" ")
|
||||||
|
|
||||||
for (nameWord in trackNameWords) {
|
for (nameWord in trackNameWords) {
|
||||||
if (nameWord.isNotBlank() && FuzzySearch.partialRatio(
|
if (nameWord.isNotBlank() && FuzzySearch.partialRatio(
|
||||||
@ -287,8 +301,8 @@ class YoutubeMusic constructor(
|
|||||||
if (result.type == "Song") {
|
if (result.type == "Song") {
|
||||||
for (artist in trackArtists) {
|
for (artist in trackArtists) {
|
||||||
if (FuzzySearch.ratio(
|
if (FuzzySearch.ratio(
|
||||||
artist.lowercase(),
|
artist.toLowerCase(),
|
||||||
result.artist?.lowercase() ?: ""
|
result.artist?.toLowerCase() ?: ""
|
||||||
) > 85
|
) > 85
|
||||||
)
|
)
|
||||||
artistMatchNumber++
|
artistMatchNumber++
|
||||||
@ -296,8 +310,8 @@ class YoutubeMusic constructor(
|
|||||||
} else { // i.e. is a Video
|
} else { // i.e. is a Video
|
||||||
for (artist in trackArtists) {
|
for (artist in trackArtists) {
|
||||||
if (FuzzySearch.partialRatio(
|
if (FuzzySearch.partialRatio(
|
||||||
artist.lowercase(),
|
artist.toLowerCase(),
|
||||||
result.name?.lowercase() ?: ""
|
result.name?.toLowerCase() ?: ""
|
||||||
) > 85
|
) > 85
|
||||||
)
|
)
|
||||||
artistMatchNumber++
|
artistMatchNumber++
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package com.shabinder.common.providers
|
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.models.TrackDetails
|
||||||
import com.shabinder.common.providers.utils.CommonUtils
|
import com.shabinder.common.providers.utils.CommonUtils
|
||||||
import com.shabinder.common.providers.utils.SpotifyUtils
|
import com.shabinder.common.providers.utils.SpotifyUtils
|
||||||
import com.shabinder.common.providers.utils.SpotifyUtils.toTrackDetailsList
|
import com.shabinder.common.providers.utils.SpotifyUtils.toTrackDetailsList
|
||||||
import io.github.shabinder.runBlocking
|
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
|
import kotlin.test.Test
|
||||||
|
|
||||||
class TestSpotifyTrackMatching {
|
class TestSpotifyTrackMatching {
|
||||||
@ -17,7 +22,15 @@ class TestSpotifyTrackMatching {
|
|||||||
|
|
||||||
private val spotifyToken: String?
|
private val spotifyToken: String?
|
||||||
get() = null
|
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
|
@Test
|
||||||
fun matchVideo() = runBlocking {
|
fun matchVideo() = runBlocking {
|
||||||
|
@ -33,9 +33,10 @@ fun org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.generateFramewor
|
|||||||
export(project(":common:providers"))
|
export(project(":common:providers"))
|
||||||
export(project(":common:list"))
|
export(project(":common:list"))
|
||||||
export(project(":common:preference"))
|
export(project(":common:preference"))
|
||||||
export(Decompose.decompose)
|
with(deps) {
|
||||||
export(MVIKotlin.mvikotlinMain)
|
export(decompose.dep)
|
||||||
export(MVIKotlin.mvikotlinLogging)
|
export(bundles.mviKotlin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +72,6 @@ kotlin {
|
|||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
implementation(project(":common:core-components"))
|
implementation(project(":common:core-components"))
|
||||||
implementation(project(":common:preference"))
|
implementation(project(":common:preference"))
|
||||||
implementation(SqlDelight.coroutineExtensions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,9 +86,10 @@ kotlin {
|
|||||||
api(project(":common:list"))
|
api(project(":common:list"))
|
||||||
api(project(":common:main"))
|
api(project(":common:main"))
|
||||||
api(project(":common:preference"))
|
api(project(":common:preference"))
|
||||||
api(Decompose.decompose)
|
with(deps) {
|
||||||
api(MVIKotlin.mvikotlinMain)
|
api(decompose.dep)
|
||||||
api(MVIKotlin.mvikotlinLogging)
|
api(bundles.mviKotlin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +101,10 @@ val packForXcode by tasks.creating(Sync::class) {
|
|||||||
group = "build"
|
group = "build"
|
||||||
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
||||||
val targetName = "ios"
|
val targetName = "ios"
|
||||||
val framework = kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(targetName)
|
val framework =
|
||||||
|
kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(
|
||||||
|
targetName
|
||||||
|
)
|
||||||
.binaries.getFramework(mode)
|
.binaries.getFramework(mode)
|
||||||
inputs.property("mode", mode)
|
inputs.property("mode", mode)
|
||||||
dependsOn(framework.linkTask)
|
dependsOn(framework.linkTask)
|
||||||
|
@ -19,6 +19,7 @@ application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
with(deps) {
|
||||||
implementation(Koin.core)
|
implementation(Koin.core)
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
@ -40,7 +41,6 @@ dependencies {
|
|||||||
implementation(Koin.core)
|
implementation(Koin.core)
|
||||||
|
|
||||||
// Matomo
|
// Matomo
|
||||||
implementation("org.piwik.java.tracking:matomo-java-tracker:1.6")
|
|
||||||
|
|
||||||
implementation(Ktor.slf4j)
|
implementation(Ktor.slf4j)
|
||||||
implementation(Ktor.clientCore)
|
implementation(Ktor.clientCore)
|
||||||
@ -52,6 +52,7 @@ dependencies {
|
|||||||
// testDeps
|
// testDeps
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
|
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
|
@ -32,9 +32,12 @@ kotlin {
|
|||||||
kotlinOptions.jvmTarget = "1.8"
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tasks.named<Copy>("jvmProcessResources") {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
|
resources.srcDirs("../common/data-models/src/main/res")
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
@ -44,19 +47,21 @@ kotlin {
|
|||||||
implementation(project(":common:compose"))
|
implementation(project(":common:compose"))
|
||||||
implementation(project(":common:providers"))
|
implementation(project(":common:providers"))
|
||||||
implementation(project(":common:root"))
|
implementation(project(":common:root"))
|
||||||
implementation("com.github.kokorin.jaffree:jaffree:2021.08.16")
|
|
||||||
|
|
||||||
// Decompose
|
with(deps) {
|
||||||
implementation(Decompose.decompose)
|
implementation(jaffree)
|
||||||
implementation(Decompose.extensionsCompose)
|
|
||||||
|
|
||||||
// MVI
|
with(decompose) {
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(dep)
|
||||||
implementation(MVIKotlin.mvikotlinMain)
|
implementation(extensions.compose)
|
||||||
|
}
|
||||||
// Koin
|
with(mviKotlin) {
|
||||||
implementation(Koin.core)
|
implementation(dep)
|
||||||
|
implementation(main)
|
||||||
|
}
|
||||||
|
|
||||||
|
implementation(koin.core)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jvmTest by getting
|
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
|
minSdk = Versions.minSdkVersion
|
||||||
targetSdk = Versions.targetSdkVersion
|
targetSdk = Versions.targetSdkVersion
|
||||||
// versionCode = Versions.versionCode
|
|
||||||
// versionName = Versions.versionName
|
|
||||||
|
|
||||||
/*ndk {
|
/*ndk {
|
||||||
abiFilters.addAll(setOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a"))
|
abiFilters.addAll(setOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a"))
|
||||||
|
@ -2,6 +2,7 @@ package nl.bravobit.ffmpeg;
|
|||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class CpuArchHelper {
|
public class CpuArchHelper {
|
||||||
public static final String X86_CPU = "x86";
|
public static final String X86_CPU = "x86";
|
||||||
public static final String X86_64_CPU = "x86_64";
|
public static final String X86_64_CPU = "x86_64";
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package nl.bravobit.ffmpeg;
|
package nl.bravobit.ffmpeg;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@ -9,10 +8,12 @@ import java.io.OutputStream;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> implements FFtask {
|
class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> implements FFtask {
|
||||||
|
|
||||||
private final String[] cmd;
|
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 FFcommandExecuteResponseHandler ffmpegExecuteResponseHandler;
|
||||||
private final ShellCommand shellCommand;
|
private final ShellCommand shellCommand;
|
||||||
private final long timeout;
|
private final long timeout;
|
||||||
@ -39,6 +40,7 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandResult doInBackground(Void... params) {
|
protected CommandResult doInBackground(Void... params) {
|
||||||
|
CommandResult ret = CommandResult.getDummyFailureResponse();
|
||||||
try {
|
try {
|
||||||
process = shellCommand.run(cmd, environment);
|
process = shellCommand.run(cmd, environment);
|
||||||
if (process == null) {
|
if (process == null) {
|
||||||
@ -46,16 +48,19 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
|
|||||||
}
|
}
|
||||||
Log.d("Running publishing updates method");
|
Log.d("Running publishing updates method");
|
||||||
checkAndUpdateProcess();
|
checkAndUpdateProcess();
|
||||||
return CommandResult.getOutputFromProcess(process);
|
ret = CommandResult.getOutputFromProcess(process);
|
||||||
|
outputStringBuilder.append(ret.output);
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
Log.e("FFmpeg binary timed out", 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) {
|
} catch (Exception e) {
|
||||||
Log.e("Error running FFmpeg binary", e);
|
Log.e("Error running FFmpeg binary", e);
|
||||||
} finally {
|
} finally {
|
||||||
Util.destroyProcess(process);
|
Util.destroyProcess(process);
|
||||||
}
|
}
|
||||||
return CommandResult.getDummyFailureResponse();
|
output = outputStringBuilder.toString();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -68,7 +73,6 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(CommandResult commandResult) {
|
protected void onPostExecute(CommandResult commandResult) {
|
||||||
if (ffmpegExecuteResponseHandler != null) {
|
if (ffmpegExecuteResponseHandler != null) {
|
||||||
output += commandResult.output;
|
|
||||||
if (commandResult.success) {
|
if (commandResult.success) {
|
||||||
ffmpegExecuteResponseHandler.onSuccess(output);
|
ffmpegExecuteResponseHandler.onSuccess(output);
|
||||||
} else {
|
} else {
|
||||||
@ -107,7 +111,7 @@ class FFcommandExecuteAsyncTask extends AsyncTask<Void, String, CommandResult> i
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
output += line + "\n";
|
outputStringBuilder.append(line); outputStringBuilder.append("\n");
|
||||||
publishProgress(line);
|
publishProgress(line);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -6,6 +6,7 @@ import android.os.AsyncTask;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class FFprobe implements FFbinaryInterface {
|
public class FFprobe implements FFbinaryInterface {
|
||||||
|
|
||||||
private final FFbinaryContextProvider context;
|
private final FFbinaryContextProvider context;
|
||||||
@ -22,12 +23,7 @@ public class FFprobe implements FFbinaryInterface {
|
|||||||
|
|
||||||
public static FFprobe getInstance(final Context context) {
|
public static FFprobe getInstance(final Context context) {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new FFprobe(new FFbinaryContextProvider() {
|
instance = new FFprobe(() -> context);
|
||||||
@Override
|
|
||||||
public Context provide() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return instance;
|
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