diff --git a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 68421994..1cfc2fbe 100644 --- a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -58,7 +58,6 @@ import com.shabinder.common.models.TrackDetails import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.uikit.* -import com.shabinder.database.Database import com.shabinder.spotiflyer.utils.* import com.tonyodev.fetch2.Status import kotlinx.coroutines.* diff --git a/build.gradle.kts b/build.gradle.kts index f5b8cd55..305b7e55 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,6 +16,8 @@ plugins { `kotlin-dsl` + id("org.jlleitschuh.gradle.ktlint") + id("org.jlleitschuh.gradle.ktlint-idea") } allprojects { @@ -27,7 +29,7 @@ allprojects { maven(url = "https://dl.bintray.com/ekito/koin") maven(url = "https://kotlin.bintray.com/kotlinx/") maven(url = "https://dl.bintray.com/icerockdev/moko") - //maven(url = "https://kotlin.bintray.com/kotlin-js-wrappers/") + // maven(url = "https://kotlin.bintray.com/kotlin-js-wrappers/") maven(url = "https://dl.bintray.com/kotlin/kotlin-js-wrappers") maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev") } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 532dc1a3..caf506b6 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -16,7 +16,6 @@ plugins { `kotlin-dsl` - //`kotlin-dsl-precompiled-script-plugins` } group = "com.shabinder" @@ -28,6 +27,7 @@ repositories { mavenCentral() google() maven(url = "https://jitpack.io") + maven(url = "https://plugins.gradle.org/m2/") maven(url = "https://dl.bintray.com/kotlin/kotlin-js-wrappers") maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev") } @@ -37,6 +37,7 @@ dependencies { implementation("com.google.gms:google-services:4.3.5") implementation("com.google.firebase:perf-plugin:1.3.5") implementation("com.google.firebase:firebase-crashlytics-gradle:2.5.1") + implementation("org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktLint}") implementation(JetBrains.Compose.gradlePlugin) implementation(JetBrains.Kotlin.gradlePlugin) implementation(JetBrains.Kotlin.serialization) @@ -50,4 +51,4 @@ kotlinDslPluginOptions { kotlin { // Add Deps to compilation, so it will become available in main project sourceSets.getByName("main").kotlin.srcDir("buildSrc/src/main/kotlin") -} \ No newline at end of file +} diff --git a/buildSrc/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/buildSrc/src/main/kotlin/Versions.kt index af9f2e27..be862598 100644 --- a/buildSrc/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/buildSrc/src/main/kotlin/Versions.kt @@ -21,26 +21,29 @@ object Versions { const val kotlinVersion = "1.4.31" const val coroutinesVersion = "1.4.2" - //const val compose = "1.0.0-alpha12" const val coilVersion = "0.4.1" - //DI + + // Code Formatting + const val ktLint = "10.0.0" + + // DI const val koin = "3.0.1-beta-1" - //Logger + // Logger const val kermit = "0.1.8" - //Internet + // Internet const val ktor = "1.5.2" const val kotlinxSerialization = "1.1.0-RC" - //Database + // Database const val sqlDelight = "1.4.4" const val sqliteJdbcDriver = "3.30.1" const val slf4j = "1.7.30" - //Android + // Android const val versionCode = 15 const val minSdkVersion = 24 const val compileSdkVersion = 29 @@ -53,7 +56,7 @@ object Koin { val android = "io.insert-koin:koin-android:${Versions.koin}" val compose = "io.insert-koin:koin-androidx-compose:${Versions.koin}" } -object Androidx{ +object Androidx { const val androidxActivity = "androidx.activity:activity-compose:1.3.0-alpha02" const val core = "androidx.core:core-ktx:1.3.2" const val palette = "androidx.palette:palette-ktx:1.0.0" @@ -162,4 +165,4 @@ object 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}" -} \ No newline at end of file +} diff --git a/buildSrc/src/main/kotlin/android-setup.gradle.kts b/buildSrc/src/main/kotlin/android-setup.gradle.kts index 1766f295..e932119b 100644 --- a/buildSrc/src/main/kotlin/android-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/android-setup.gradle.kts @@ -16,6 +16,7 @@ plugins { id("com.android.library") + id("ktlint-setup") } android { @@ -43,5 +44,4 @@ android { res.srcDirs("src/androidMain/res") } } - } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/NetworkResponse.kt b/buildSrc/src/main/kotlin/ktlint-setup.gradle.kts similarity index 50% rename from common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/NetworkResponse.kt rename to buildSrc/src/main/kotlin/ktlint-setup.gradle.kts index 7c082001..972eee6b 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/NetworkResponse.kt +++ b/buildSrc/src/main/kotlin/ktlint-setup.gradle.kts @@ -14,9 +14,31 @@ * * along with this program. If not, see . */ -package com.shabinder.common.di +plugins { + id("org.jlleitschuh.gradle.ktlint") + id("org.jlleitschuh.gradle.ktlint-idea") +} -sealed class NetworkResponse { - data class Success(val value:T):NetworkResponse() - data class Error(val message:String):NetworkResponse() +subprojects { + apply(plugin = "org.jlleitschuh.gradle.ktlint") + apply(plugin = "org.jlleitschuh.gradle.ktlint-idea") + repositories { + // Required to download KtLint + mavenCentral() + } + ktlint { + android.set(true) + outputToConsole.set(true) + ignoreFailures.set(true) + coloredOutput.set(true) + verbose.set(true) + filter { + exclude("**/generated/**") + exclude("**/build/**") + } + } + // Optionally configure plugin + /*configure { + debug.set(true) + }*/ } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts b/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts index d050dc06..19232ab7 100644 --- a/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts @@ -18,6 +18,7 @@ plugins { id("com.android.library") id("kotlin-multiplatform") id("org.jetbrains.compose") + id("ktlint-setup") } kotlin { diff --git a/buildSrc/src/main/kotlin/multiplatform-setup-test.gradle.kts b/buildSrc/src/main/kotlin/multiplatform-setup-test.gradle.kts index 3ceb2e98..21df8da8 100644 --- a/buildSrc/src/main/kotlin/multiplatform-setup-test.gradle.kts +++ b/buildSrc/src/main/kotlin/multiplatform-setup-test.gradle.kts @@ -17,15 +17,16 @@ plugins { id("com.android.library") id("kotlin-multiplatform") + id("ktlint-setup") } kotlin { jvm("desktop") android() - //ios() + // ios() js() { browser() - //nodejs() + // nodejs() binaries.executable() } sourceSets { @@ -47,9 +48,7 @@ kotlin { } } named("jsTest") { - dependencies { - - } + dependencies {} } } diff --git a/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts b/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts index a216a575..2e7d5c85 100644 --- a/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts @@ -14,17 +14,11 @@ * * along with this program. If not, see . */ -import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.compose -import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.kotlin -import gradle.kotlin.dsl.accessors._2e23d8fadf0ed92ae13e19db3d83f86d.sourceSets -import org.gradle.kotlin.dsl.withType -import org.jetbrains.compose.compose - plugins { -// id("com.android.library") id("android-setup") id("kotlin-multiplatform") id("org.jetbrains.compose") + id("ktlint-setup") } kotlin { @@ -32,14 +26,12 @@ kotlin { android() js() { browser() - //nodejs() + // nodejs() binaries.executable() } sourceSets { named("commonMain") { - dependencies { - - } + dependencies {} } named("androidMain") { diff --git a/common/compose/build.gradle.kts b/common/compose/build.gradle.kts index 48dac2a5..3de1f191 100644 --- a/common/compose/build.gradle.kts +++ b/common/compose/build.gradle.kts @@ -33,7 +33,7 @@ kotlin { implementation(project(":common:database")) implementation(project(":common:data-models")) implementation(project(":common:dependency-injection")) - //DECOMPOSE + // DECOMPOSE implementation(Decompose.decompose) implementation(Decompose.extensionsCompose) } diff --git a/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/AndroidImages.kt b/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/AndroidImages.kt index 55e6ddcf..f459ab12 100644 --- a/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/AndroidImages.kt +++ b/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/AndroidImages.kt @@ -21,7 +21,13 @@ package com.shabinder.common.uikit import androidx.annotation.DrawableRes import androidx.compose.animation.Crossfade import androidx.compose.foundation.Image -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.vector.ImageVector @@ -39,21 +45,21 @@ import kotlinx.coroutines.withContext @Composable actual fun ImageLoad( - link:String, - loader:suspend (String) -> Picture, + link: String, + loader: suspend (String) -> Picture, desc: String, - modifier:Modifier, - //placeholder: ImageVector + modifier: Modifier, + // placeholder: ImageVector ) { var pic by remember(link) { mutableStateOf(null) } - LaunchedEffect(link){ + LaunchedEffect(link) { withContext(dispatcherIO) { pic = loader(link).image } } - Crossfade(pic){ - if(it == null) Image(PlaceHolderImage(), desc, modifier,contentScale = ContentScale.Crop) else Image(it, desc, modifier,contentScale = ContentScale.Crop) + Crossfade(pic) { + if (it == null) Image(PlaceHolderImage(), desc, modifier, contentScale = ContentScale.Crop) else Image(it, desc, modifier, contentScale = ContentScale.Crop) } } @@ -68,9 +74,8 @@ actual fun pristineFont() = FontFamily( Font(R.font.pristine_script, FontWeight.Bold) ) - @Composable -actual fun DownloadImageTick(){ +actual fun DownloadImageTick() { Image( painterResource(R.drawable.ic_tick), "Download Done" @@ -78,7 +83,7 @@ actual fun DownloadImageTick(){ } @Composable -actual fun DownloadImageError(){ +actual fun DownloadImageError() { Image( painterResource(R.drawable.ic_error), "Error! Cant Download this track" @@ -86,7 +91,7 @@ actual fun DownloadImageError(){ } @Composable -actual fun DownloadImageArrow(modifier: Modifier){ +actual fun DownloadImageArrow(modifier: Modifier) { Image( painterResource(R.drawable.ic_arrow), "Start Download", @@ -125,16 +130,16 @@ actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo) actual fun GithubLogo() = vectorResource(R.drawable.ic_github) @Composable -fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id) +fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id) @Composable actual fun Toast( text: String, visibility: MutableState, duration: ToastDuration -){ - //We Have Android's Implementation of Toast so its just Empty +) { + // We Have Android's Implementation of Toast so its just Empty +} +actual fun showPopUpMessage(text: String) { + android.widget.Toast.makeText(appContext, text, android.widget.Toast.LENGTH_SHORT).show() } -actual fun showPopUpMessage(text: String){ - android.widget.Toast.makeText(appContext,text, android.widget.Toast.LENGTH_SHORT).show() -} \ No newline at end of file diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/ExpectImages.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/ExpectImages.kt index c71d658a..f4f66bbe 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/ExpectImages.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/ExpectImages.kt @@ -17,55 +17,55 @@ @file:Suppress("FunctionName") package com.shabinder.common.uikit -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import com.shabinder.common.di.Picture @Composable expect fun ImageLoad( - link:String, - loader:suspend (String) ->Picture, + link: String, + loader: suspend (String) -> Picture, desc: String = "Album Art", - modifier:Modifier = Modifier, - //placeholder:ImageVector = PlaceHolderImage() + modifier: Modifier = Modifier, + // placeholder:ImageVector = PlaceHolderImage() ) @Composable expect fun DownloadImageTick() @Composable -expect fun DownloadAllImage():ImageVector +expect fun DownloadAllImage(): ImageVector @Composable -expect fun ShareImage():ImageVector +expect fun ShareImage(): ImageVector @Composable -expect fun PlaceHolderImage():ImageVector +expect fun PlaceHolderImage(): ImageVector @Composable -expect fun SpotiFlyerLogo():ImageVector +expect fun SpotiFlyerLogo(): ImageVector @Composable -expect fun SpotifyLogo():ImageVector +expect fun SpotifyLogo(): ImageVector @Composable -expect fun YoutubeLogo():ImageVector +expect fun YoutubeLogo(): ImageVector @Composable -expect fun GaanaLogo():ImageVector +expect fun GaanaLogo(): ImageVector @Composable -expect fun YoutubeMusicLogo():ImageVector +expect fun YoutubeMusicLogo(): ImageVector @Composable -expect fun GithubLogo():ImageVector +expect fun GithubLogo(): ImageVector @Composable -expect fun HeartIcon():ImageVector +expect fun HeartIcon(): ImageVector @Composable expect fun DownloadImageError() @Composable -expect fun DownloadImageArrow(modifier: Modifier) \ No newline at end of file +expect fun DownloadImageArrow(modifier: Modifier) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Shape.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Shape.kt index 3bfc97fa..d37cbc51 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Shape.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Shape.kt @@ -21,7 +21,7 @@ import androidx.compose.material.Shapes import androidx.compose.ui.unit.dp val SpotiFlyerShapes = Shapes( - small = RoundedCornerShape(percent = 50), - medium = RoundedCornerShape(size = 8.dp), - large = RoundedCornerShape(size = 0.dp) -) \ No newline at end of file + small = RoundedCornerShape(percent = 50), + medium = RoundedCornerShape(size = 8.dp), + large = RoundedCornerShape(size = 0.dp) +) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt index 004b06a8..42e5474d 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt @@ -17,16 +17,30 @@ package com.shabinder.common.uikit import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.ExtendedFloatingActionButton +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -35,7 +49,6 @@ import com.shabinder.common.di.Picture import com.shabinder.common.list.SpotiFlyerList import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails -import kotlinx.coroutines.CoroutineScope @Composable fun SpotiFlyerListContent( @@ -44,23 +57,21 @@ fun SpotiFlyerListContent( ) { val model by component.models.collectAsState(SpotiFlyerList.State()) - val coroutineScope = rememberCoroutineScope() - Box(modifier = modifier.fillMaxSize()) { - //TODO Better Null Handling + // TODO Better Null Handling val result = model.queryResult - if(result == null){ - Column(Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) { + if (result == null) { + Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { CircularProgressIndicator() Spacer(modifier.padding(8.dp)) - Text("Loading..",style = appNameStyle,color = colorPrimary) + Text("Loading..", style = appNameStyle, color = colorPrimary) } - }else{ + } else { LazyColumn( verticalArrangement = Arrangement.spacedBy(12.dp), content = { item { - CoverImage(result.title, result.coverUrl, coroutineScope,component::loadImage) + CoverImage(result.title, result.coverUrl, component::loadImage) } itemsIndexed(model.trackList) { index, item -> TrackCard( @@ -73,7 +84,7 @@ fun SpotiFlyerListContent( modifier = Modifier.fillMaxSize(), ) DownloadAllButton( - onClick = {component.onDownloadAllClicked(model.trackList)}, + onClick = { component.onDownloadAllClicked(model.trackList) }, modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter) ) } @@ -83,10 +94,10 @@ fun SpotiFlyerListContent( @Composable fun TrackCard( track: TrackDetails, - downloadTrack:()->Unit, - loadImage:suspend (String)-> Picture + downloadTrack: () -> Unit, + loadImage: suspend (String) -> Picture ) { - Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { ImageLoad( track.albumArtURL, loadImage, @@ -96,18 +107,18 @@ fun TrackCard( .height(70.dp) .clip(MaterialTheme.shapes.medium) ) - Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { - Text(track.title,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent) + Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f), verticalArrangement = Arrangement.SpaceEvenly) { + Text(track.title, maxLines = 1, overflow = TextOverflow.Ellipsis, style = SpotiFlyerTypography.h6, color = colorAccent) Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.Bottom, modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize() - ){ - Text("${track.artists.firstOrNull()}...",fontSize = 12.sp,maxLines = 1) - Text("${track.durationSec/60} min, ${track.durationSec%60} sec",fontSize = 12.sp,maxLines = 1,overflow = TextOverflow.Ellipsis) + ) { + Text("${track.artists.firstOrNull()}...", fontSize = 12.sp, maxLines = 1) + Text("${track.durationSec / 60} min, ${track.durationSec % 60} sec", fontSize = 12.sp, maxLines = 1, overflow = TextOverflow.Ellipsis) } } - when(track.downloaded){ + when (track.downloaded) { is DownloadStatus.Downloaded -> { DownloadImageTick() } @@ -118,15 +129,19 @@ fun TrackCard( DownloadImageError() } is DownloadStatus.Downloading -> { - CircularProgressIndicator(progress = (track.downloaded as DownloadStatus.Downloading).progress.toFloat()/100f) + CircularProgressIndicator(progress = (track.downloaded as DownloadStatus.Downloading).progress.toFloat() / 100f) } is DownloadStatus.Converting -> { - CircularProgressIndicator(progress = 100f,color = colorAccent) + CircularProgressIndicator(progress = 100f, color = colorAccent) } is DownloadStatus.NotDownloaded -> { - DownloadImageArrow(Modifier.clickable(onClick = { - downloadTrack() - })) + DownloadImageArrow( + Modifier.clickable( + onClick = { + downloadTrack() + } + ) + ) } } } @@ -136,7 +151,6 @@ fun TrackCard( fun CoverImage( title: String, coverURL: String, - scope: CoroutineScope, loadImage: suspend (String) -> Picture, modifier: Modifier = Modifier, ) { @@ -160,7 +174,6 @@ fun CoverImage( maxLines = 2, textAlign = TextAlign.Center, overflow = TextOverflow.Ellipsis, - //color = colorAccent, ) } /*scope.launch { @@ -173,8 +186,8 @@ fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) { ExtendedFloatingActionButton( text = { Text("Download All") }, onClick = onClick, - icon = { Icon(imageVector = DownloadAllImage(),"Download All Button",tint = Color(0xFF000000)) }, + icon = { Icon(imageVector = DownloadAllImage(), "Download All Button", tint = Color(0xFF000000)) }, backgroundColor = colorAccent, modifier = modifier ) -} \ No newline at end of file +} diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt index 2936511f..c76d32af 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt @@ -17,26 +17,54 @@ package com.shabinder.common.uikit import androidx.compose.animation.Crossfade -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.* +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Tab +import androidx.compose.material.TabPosition +import androidx.compose.material.TabRow import androidx.compose.material.TabRowDefaults.tabIndicatorOffset +import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.material.TextFieldDefaults.textFieldColors import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Info -import androidx.compose.material.icons.rounded.* -import androidx.compose.runtime.* +import androidx.compose.material.icons.rounded.CardGiftcard +import androidx.compose.material.icons.rounded.Edit +import androidx.compose.material.icons.rounded.Flag +import androidx.compose.material.icons.rounded.Share +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType @@ -53,7 +81,7 @@ import com.shabinder.common.main.SpotiFlyerMain.HomeCategory import com.shabinder.common.models.DownloadRecord @Composable -fun SpotiFlyerMainContent(component: SpotiFlyerMain){ +fun SpotiFlyerMainContent(component: SpotiFlyerMain) { val model by component.models.collectAsState(SpotiFlyerMain.State()) Column { @@ -69,7 +97,7 @@ fun SpotiFlyerMainContent(component: SpotiFlyerMain){ component::selectCategory ) - when(model.selectedCategory){ + when (model.selectedCategory) { HomeCategory.About -> AboutColumn() HomeCategory.History -> HistoryColumn( model.records.sortedByDescending { it.id }, @@ -87,7 +115,7 @@ fun HomeTabBar( selectCategory: (HomeCategory) -> Unit, modifier: Modifier = Modifier ) { - val selectedIndex =categories.indexOfFirst { it == selectedCategory } + val selectedIndex = categories.indexOfFirst { it == selectedCategory } val indicator = @Composable { tabPositions: List -> HomeCategoryTabIndicator( Modifier.tabIndicatorOffset(tabPositions[selectedIndex]) @@ -99,57 +127,62 @@ fun HomeTabBar( indicator = indicator, modifier = modifier, ) { - categories.forEachIndexed { index, category -> - Tab( - selected = index == selectedIndex, - onClick = { selectCategory(category) }, - text = { - Text( - text = when (category) { - HomeCategory.About -> "About" - HomeCategory.History -> "History" - }, - style = MaterialTheme.typography.body2 - ) - }, - icon = { - when (category) { - HomeCategory.About -> Icon(Icons.Outlined.Info,"Info Tab") - HomeCategory.History -> Icon(Icons.Outlined.History,"History Tab") - } + categories.forEachIndexed { index, category -> + Tab( + selected = index == selectedIndex, + onClick = { selectCategory(category) }, + text = { + Text( + text = when (category) { + HomeCategory.About -> "About" + HomeCategory.History -> "History" + }, + style = MaterialTheme.typography.body2 + ) + }, + icon = { + when (category) { + HomeCategory.About -> Icon(Icons.Outlined.Info, "Info Tab") + HomeCategory.History -> Icon(Icons.Outlined.History, "History Tab") } - ) - } + } + ) } + } } @Composable fun SearchPanel( - link:String, - updateLink:(String) -> Unit, - onSearch:(String) -> Unit, + link: String, + updateLink: (String) -> Unit, + onSearch: (String) -> Unit, modifier: Modifier = Modifier -){ +) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(top = 16.dp) - ){ + ) { TextField( value = link, - onValueChange = updateLink , + onValueChange = updateLink, leadingIcon = { - Icon(Icons.Rounded.Edit,"Link Text Box",tint = Color.LightGray) + Icon(Icons.Rounded.Edit, "Link Text Box", tint = Color.LightGray) }, - label = { Text(text = "Paste Link Here...",color = Color.LightGray) }, + label = { Text(text = "Paste Link Here...", color = Color.LightGray) }, singleLine = true, - textStyle = TextStyle.Default.merge(TextStyle(fontSize = 18.sp,color = Color.White)), + textStyle = TextStyle.Default.merge(TextStyle(fontSize = 18.sp, color = Color.White)), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), modifier = modifier.padding(12.dp).fillMaxWidth() .border( - BorderStroke(2.dp, Brush.horizontalGradient(listOf( - colorPrimary, - colorAccent - ))), + BorderStroke( + 2.dp, + Brush.horizontalGradient( + listOf( + colorPrimary, + colorAccent + ) + ) + ), RoundedCornerShape(30.dp) ), shape = RoundedCornerShape(size = 30.dp), @@ -162,29 +195,34 @@ fun SearchPanel( OutlinedButton( modifier = Modifier.padding(12.dp).wrapContentWidth(), onClick = { - if(link.isBlank()) showPopUpMessage("Enter A Link!") - else{ - //TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else + if (link.isBlank()) showPopUpMessage("Enter A Link!") + else { + // TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else onSearch(link) } }, - border = BorderStroke(1.dp, Brush.horizontalGradient(listOf( - colorPrimary, - colorAccent - ))) - ){ - Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp)) + border = BorderStroke( + 1.dp, + Brush.horizontalGradient( + listOf( + colorPrimary, + colorAccent + ) + ) + ) + ) { + Text(text = "Search", style = SpotiFlyerTypography.h6, modifier = Modifier.padding(4.dp)) } } } @Composable fun AboutColumn(modifier: Modifier = Modifier) { - //TODO Make Scrollable + // TODO Make Scrollable Column(modifier.fillMaxSize().verticalScroll(rememberScrollState())) { Card( modifier = modifier.fillMaxWidth(), - border = BorderStroke(1.dp,Color.Gray) + border = BorderStroke(1.dp, Color.Gray) ) { Column(modifier.padding(12.dp)) { Text( @@ -193,34 +231,41 @@ fun AboutColumn(modifier: Modifier = Modifier) { color = colorAccent ) Spacer(modifier = Modifier.padding(top = 12.dp)) - Row(horizontalArrangement = Arrangement.Center,modifier = modifier.fillMaxWidth()) { + Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) { Icon( imageVector = SpotifyLogo(), "Open Spotify", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( - onClick = { openPlatform("com.spotify.music","http://open.spotify.com") }) + onClick = { openPlatform("com.spotify.music", "http://open.spotify.com") } + ) ) Spacer(modifier = modifier.padding(start = 16.dp)) - Icon(imageVector = GaanaLogo(), + Icon( + imageVector = GaanaLogo(), "Open Gaana", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( - onClick = { openPlatform("com.gaana","http://gaana.com") }) + onClick = { openPlatform("com.gaana", "http://gaana.com") } + ) ) Spacer(modifier = modifier.padding(start = 16.dp)) - Icon(imageVector = YoutubeLogo(), + Icon( + imageVector = YoutubeLogo(), "Open Youtube", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( - onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com") }) + onClick = { openPlatform("com.google.android.youtube", "http://m.youtube.com") } + ) ) Spacer(modifier = modifier.padding(start = 12.dp)) - Icon(imageVector = YoutubeMusicLogo(), + Icon( + imageVector = YoutubeMusicLogo(), "Open Youtube Music", tint = Color.Unspecified, modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( - onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/") }) + onClick = { openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") } + ) ) } } @@ -228,7 +273,7 @@ fun AboutColumn(modifier: Modifier = Modifier) { Spacer(modifier = Modifier.padding(top = 8.dp)) Card( modifier = modifier.fillMaxWidth(), - border = BorderStroke(1.dp,Color.Gray)//Gray + border = BorderStroke(1.dp, Color.Gray) // Gray ) { Column(modifier.padding(12.dp)) { Text( @@ -237,12 +282,14 @@ fun AboutColumn(modifier: Modifier = Modifier) { color = colorAccent ) Spacer(modifier = Modifier.padding(top = 6.dp)) - Row(verticalAlignment = Alignment.CenterVertically, + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().clickable( - onClick = { openPlatform("","http://github.com/Shabinder/SpotiFlyer") }) + onClick = { openPlatform("", "http://github.com/Shabinder/SpotiFlyer") } + ) .padding(vertical = 6.dp) ) { - Icon(imageVector = GithubLogo(),"Open Project Repo",tint = Color(0xFFCCCCCC)) + Icon(imageVector = GithubLogo(), "Open Project Repo", tint = Color(0xFFCCCCCC)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -257,10 +304,10 @@ fun AboutColumn(modifier: Modifier = Modifier) { } Row( modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable(onClick = { openPlatform("","http://github.com/Shabinder/SpotiFlyer") }), + .clickable(onClick = { openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Flag,"Help Translate",Modifier.size(32.dp)) + Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp)) Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -278,7 +325,7 @@ fun AboutColumn(modifier: Modifier = Modifier) { .clickable(onClick = { giveDonation() }), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.CardGiftcard,"Support Developer") + Icon(Icons.Rounded.CardGiftcard, "Support Developer") Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -293,12 +340,14 @@ fun AboutColumn(modifier: Modifier = Modifier) { } Row( modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable(onClick = { - shareApp() - }), + .clickable( + onClick = { + shareApp() + } + ), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.Share,"Share SpotiFlyer App") + Icon(Icons.Rounded.Share, "Share SpotiFlyer App") Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -319,22 +368,23 @@ fun AboutColumn(modifier: Modifier = Modifier) { @Composable fun HistoryColumn( list: List, - loadImage:suspend (String)-> Picture, + loadImage: suspend (String) -> Picture, onItemClicked: (String) -> Unit ) { - Crossfade(list){ - if(it.isEmpty()){ - Column(Modifier.padding(bottom = 32.dp).fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) { - Icon(Icons.Outlined.Info,"No History Available Yet",modifier = Modifier.size(80.dp), + Crossfade(list) { + if (it.isEmpty()) { + Column(Modifier.padding(bottom = 32.dp).fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + Icons.Outlined.Info, "No History Available Yet", modifier = Modifier.size(80.dp), colorOffWhite ) - Text("No History Available",style = SpotiFlyerTypography.h4.copy(fontWeight = FontWeight.Light),textAlign = TextAlign.Center) + Text("No History Available", style = SpotiFlyerTypography.h4.copy(fontWeight = FontWeight.Light), textAlign = TextAlign.Center) } - }else{ + } else { LazyColumn( verticalArrangement = Arrangement.spacedBy(12.dp), content = { - items(it.distinctBy {record -> record.coverUrl }) { record -> + items(it.distinctBy { record -> record.coverUrl }) { record -> DownloadRecordItem( item = record, loadImage, @@ -351,39 +401,40 @@ fun HistoryColumn( @Composable fun DownloadRecordItem( item: DownloadRecord, - loadImage:suspend (String)-> Picture, - onItemClicked:(String)->Unit + loadImage: suspend (String) -> Picture, + onItemClicked: (String) -> Unit ) { - Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { ImageLoad( item.coverUrl, loadImage, "Album Art", modifier = Modifier.height(70.dp).width(70.dp).clip(SpotiFlyerShapes.medium) ) - Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { - Text(item.name,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent) + Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f), verticalArrangement = Arrangement.SpaceEvenly) { + Text(item.name, maxLines = 1, overflow = TextOverflow.Ellipsis, style = SpotiFlyerTypography.h6, color = colorAccent) Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.Bottom, modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize() - ){ - Text(item.type,fontSize = 13.sp,color = colorOffWhite) - Text("Tracks: ${item.totalFiles}",fontSize = 13.sp,color = colorOffWhite) + ) { + Text(item.type, fontSize = 13.sp, color = colorOffWhite) + Text("Tracks: ${item.totalFiles}", fontSize = 13.sp, color = colorOffWhite) } } Image( imageVector = ShareImage(), "Research", - modifier = Modifier.clickable(onClick = { - //if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else - onItemClicked(item.link) - }) + modifier = Modifier.clickable( + onClick = { + // if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else + onItemClicked(item.link) + } + ) ) } } - @Composable fun HomeCategoryTabIndicator( modifier: Modifier = Modifier, @@ -394,4 +445,4 @@ fun HomeCategoryTabIndicator( .height(4.dp) .background(color, RoundedCornerShape(topStartPercent = 100, topEndPercent = 100)) ) -} \ No newline at end of file +} diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt index 12b5d108..ba39886b 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt @@ -16,11 +16,24 @@ package com.shabinder.common.uikit -import androidx.compose.animation.core.* +import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.Spring.StiffnessLow +import androidx.compose.animation.core.animateDp +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TopAppBar @@ -45,7 +58,7 @@ import com.shabinder.common.uikit.utils.verticalGradientScrim private var isSplashShown = SplashState.Shown @Composable -fun SpotiFlyerRootContent(component: SpotiFlyerRoot, statusBarHeight:Dp = 0.dp): SpotiFlyerRoot { +fun SpotiFlyerRootContent(component: SpotiFlyerRoot, statusBarHeight: Dp = 0.dp): SpotiFlyerRoot { val transitionState = remember { MutableTransitionState(SplashState.Shown) } val transition = updateTransition(transitionState) @@ -63,10 +76,10 @@ fun SpotiFlyerRootContent(component: SpotiFlyerRoot, statusBarHeight:Dp = 0.dp): val contentTopPadding by transition.animateDp( transitionSpec = { spring(stiffness = StiffnessLow) } ) { - if (it == SplashState.Shown && isSplashShown == SplashState.Shown) 100.dp else 0.dp + if (it == SplashState.Shown && isSplashShown == SplashState.Shown) 100.dp else 0.dp } - Box{ + Box { Splash( modifier = Modifier.alpha(splashAlpha), onTimeout = { @@ -85,17 +98,17 @@ fun SpotiFlyerRootContent(component: SpotiFlyerRoot, statusBarHeight:Dp = 0.dp): } @Composable -fun MainScreen(modifier: Modifier = Modifier, topPadding: Dp = 0.dp,statusBarHeight: Dp = 0.dp,component: SpotiFlyerRoot) { +fun MainScreen(modifier: Modifier = Modifier, topPadding: Dp = 0.dp, statusBarHeight: Dp = 0.dp, component: SpotiFlyerRoot) { val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.65f) Column( modifier = modifier.fillMaxSize() - .verticalGradientScrim( - color = colorPrimaryDark.copy(alpha = 0.38f), - startYPercentage = 0.29f, - endYPercentage = 0f, - ) + .verticalGradientScrim( + color = colorPrimaryDark.copy(alpha = 0.38f), + startYPercentage = 0.29f, + endYPercentage = 0f, + ) ) { Spacer(Modifier.background(appBarColor).height(statusBarHeight).fillMaxWidth()) LocalViewConfiguration.current @@ -136,7 +149,7 @@ fun AppBar( style = appNameStyle ) } - },/* + }, /* actions = { IconButton( onClick = { *//*TODO: Open Preferences*//* } diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Theme.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Theme.kt index a62c7913..fdf13ddf 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Theme.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Theme.kt @@ -20,11 +20,11 @@ import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable @Composable -fun SpotiFlyerTheme(content: @Composable() () -> Unit) { +fun SpotiFlyerTheme(content: @Composable () -> Unit) { MaterialTheme( - colors = SpotiFlyerColors, - typography = SpotiFlyerTypography, - shapes = SpotiFlyerShapes, - content = content + colors = SpotiFlyerColors, + typography = SpotiFlyerTypography, + shapes = SpotiFlyerShapes, + content = content ) -} \ No newline at end of file +} diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Toast.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Toast.kt index ce36a479..fa4a088e 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Toast.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Toast.kt @@ -31,4 +31,4 @@ expect fun Toast( text: String, visibility: MutableState = mutableStateOf(false), duration: ToastDuration = ToastDuration.Long -) \ No newline at end of file +) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Type.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Type.kt index 01d59ee3..b4140437 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Type.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Type.kt @@ -23,107 +23,106 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp - -expect fun montserratFont():FontFamily -expect fun pristineFont():FontFamily +expect fun montserratFont(): FontFamily +expect fun pristineFont(): FontFamily val SpotiFlyerTypography = Typography( - h1 = TextStyle( - fontFamily = montserratFont(), - fontSize = 96.sp, - fontWeight = FontWeight.Light, - lineHeight = 117.sp, - letterSpacing = (-1.5).sp - ), - h2 = TextStyle( - fontFamily = montserratFont(), - fontSize = 60.sp, - fontWeight = FontWeight.Light, - lineHeight = 73.sp, - letterSpacing = (-0.5).sp - ), - h3 = TextStyle( - fontFamily = montserratFont(), - fontSize = 48.sp, - fontWeight = FontWeight.Normal, - lineHeight = 59.sp - ), - h4 = TextStyle( - fontFamily = montserratFont(), - fontSize = 30.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 37.sp - ), - h5 = TextStyle( - fontFamily = montserratFont(), - fontSize = 24.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 29.sp - ), - h6 = TextStyle( - fontFamily = montserratFont(), - fontSize = 18.sp, - fontWeight = FontWeight.Medium, - lineHeight = 26.sp, - letterSpacing = 0.5.sp + h1 = TextStyle( + fontFamily = montserratFont(), + fontSize = 96.sp, + fontWeight = FontWeight.Light, + lineHeight = 117.sp, + letterSpacing = (-1.5).sp + ), + h2 = TextStyle( + fontFamily = montserratFont(), + fontSize = 60.sp, + fontWeight = FontWeight.Light, + lineHeight = 73.sp, + letterSpacing = (-0.5).sp + ), + h3 = TextStyle( + fontFamily = montserratFont(), + fontSize = 48.sp, + fontWeight = FontWeight.Normal, + lineHeight = 59.sp + ), + h4 = TextStyle( + fontFamily = montserratFont(), + fontSize = 30.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 37.sp + ), + h5 = TextStyle( + fontFamily = montserratFont(), + fontSize = 24.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 29.sp + ), + h6 = TextStyle( + fontFamily = montserratFont(), + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + lineHeight = 26.sp, + letterSpacing = 0.5.sp - ), - subtitle1 = TextStyle( - fontFamily = montserratFont(), - fontSize = 16.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 20.sp, - letterSpacing = 0.5.sp - ), - subtitle2 = TextStyle( - fontFamily = montserratFont(), - fontSize = 14.sp, - fontWeight = FontWeight.Medium, - lineHeight = 17.sp, - letterSpacing = 0.1.sp - ), - body1 = TextStyle( - fontFamily = montserratFont(), - fontSize = 16.sp, - fontWeight = FontWeight.Medium, - lineHeight = 20.sp, - letterSpacing = 0.15.sp, - ), - body2 = TextStyle( - fontFamily = montserratFont(), - fontSize = 14.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 20.sp, - letterSpacing = 0.25.sp - ), - button = TextStyle( - fontFamily = montserratFont(), - fontSize = 14.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 16.sp, - letterSpacing = 1.25.sp - ), - caption = TextStyle( - fontFamily = montserratFont(), - fontSize = 12.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 16.sp, - letterSpacing = 0.sp - ), - overline = TextStyle( - fontFamily = montserratFont(), - fontSize = 12.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 16.sp, - letterSpacing = 1.sp - ) + ), + subtitle1 = TextStyle( + fontFamily = montserratFont(), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 20.sp, + letterSpacing = 0.5.sp + ), + subtitle2 = TextStyle( + fontFamily = montserratFont(), + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + lineHeight = 17.sp, + letterSpacing = 0.1.sp + ), + body1 = TextStyle( + fontFamily = montserratFont(), + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + lineHeight = 20.sp, + letterSpacing = 0.15.sp, + ), + body2 = TextStyle( + fontFamily = montserratFont(), + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 20.sp, + letterSpacing = 0.25.sp + ), + button = TextStyle( + fontFamily = montserratFont(), + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 16.sp, + letterSpacing = 1.25.sp + ), + caption = TextStyle( + fontFamily = montserratFont(), + fontSize = 12.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 16.sp, + letterSpacing = 0.sp + ), + overline = TextStyle( + fontFamily = montserratFont(), + fontSize = 12.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 16.sp, + letterSpacing = 1.sp + ) ) val appNameStyle = TextStyle( - fontFamily = pristineFont(), - fontSize = 40.sp, - fontWeight = FontWeight.SemiBold, - lineHeight = 42.sp, - letterSpacing = (1.5).sp, - color = Color(0xFFECECEC) -) \ No newline at end of file + fontFamily = pristineFont(), + fontSize = 40.sp, + fontWeight = FontWeight.SemiBold, + lineHeight = 42.sp, + letterSpacing = (1.5).sp, + color = Color(0xFFECECEC) +) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/splash/Splash.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/splash/Splash.kt index fc03a7a6..40634a4a 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/splash/Splash.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/splash/Splash.kt @@ -17,7 +17,13 @@ package com.shabinder.common.uikit.splash import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -29,7 +35,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.shabinder.common.uikit.* +import com.shabinder.common.uikit.HeartIcon +import com.shabinder.common.uikit.SpotiFlyerLogo +import com.shabinder.common.uikit.SpotiFlyerTypography +import com.shabinder.common.uikit.colorAccent +import com.shabinder.common.uikit.colorPrimary import kotlinx.coroutines.delay private const val SplashWaitTime: Long = 2000 @@ -45,7 +55,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) { delay(SplashWaitTime) currentOnTimeout() } - Image(imageVector = SpotiFlyerLogo(),"SpotiFlyer Logo") + Image(imageVector = SpotiFlyerLogo(), "SpotiFlyer Logo") MadeInIndia(Modifier.align(Alignment.BottomCenter)) } } @@ -53,7 +63,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) { @Composable fun MadeInIndia( modifier: Modifier = Modifier -){ +) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier.padding(8.dp) @@ -68,7 +78,7 @@ fun MadeInIndia( fontSize = 22.sp ) Spacer(modifier = Modifier.padding(start = 4.dp)) - Icon(HeartIcon(),"Love",tint = Color.Unspecified) + Icon(HeartIcon(), "Love", tint = Color.Unspecified) Spacer(modifier = Modifier.padding(start = 4.dp)) Text( text = " in India", @@ -83,4 +93,4 @@ fun MadeInIndia( fontSize = 14.sp ) } -} \ No newline at end of file +} diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/utils/GradientScrim.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/utils/GradientScrim.kt index 327b495d..cb245a0f 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/utils/GradientScrim.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/utils/GradientScrim.kt @@ -40,8 +40,10 @@ import kotlin.math.pow */ fun Modifier.verticalGradientScrim( color: Color, - /*@FloatRange(from = 0.0, to = 1.0)*/ startYPercentage: Float = 0f, - /*@FloatRange(from = 0.0, to = 1.0)*/ endYPercentage: Float = 1f, + /*@FloatRange(from = 0.0, to = 1.0)*/ + startYPercentage: Float = 0f, + /*@FloatRange(from = 0.0, to = 1.0)*/ + endYPercentage: Float = 1f, decay: Float = 1.0f, numStops: Int = 16, fixedHeight: Float? = null diff --git a/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopImages.kt b/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopImages.kt index bb72edf2..67a1839d 100644 --- a/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopImages.kt +++ b/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopImages.kt @@ -19,7 +19,12 @@ package com.shabinder.common.uikit import androidx.compose.animation.Crossfade import androidx.compose.foundation.Image -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.vector.ImageVector @@ -34,26 +39,26 @@ import kotlinx.coroutines.withContext @Composable actual fun ImageLoad( - link:String, - loader:suspend (String) -> Picture, + link: String, + loader: suspend (String) -> Picture, desc: String, - modifier:Modifier, - //placeholder: ImageVector + modifier: Modifier, + // placeholder: ImageVector ) { var pic by remember(link) { mutableStateOf(null) } - LaunchedEffect(link){ + LaunchedEffect(link) { withContext(dispatcherIO) { pic = loader(link).image } } - Crossfade(pic){ - if(it == null) Image(PlaceHolderImage(), desc, modifier,contentScale = ContentScale.Crop) else Image(it, desc, modifier,contentScale = ContentScale.Crop) + Crossfade(pic) { + if (it == null) Image(PlaceHolderImage(), desc, modifier, contentScale = ContentScale.Crop) else Image(it, desc, modifier, contentScale = ContentScale.Crop) } } @Composable -actual fun DownloadImageTick(){ +actual fun DownloadImageTick() { Image( vectorXmlResource("drawable/ic_tick.xml"), "Downloaded" @@ -72,7 +77,7 @@ actual fun pristineFont() = FontFamily( ) @Composable -actual fun DownloadImageError(){ +actual fun DownloadImageError() { Image( vectorXmlResource("drawable/ic_error.xml"), "Can't Download" @@ -80,7 +85,7 @@ actual fun DownloadImageError(){ } @Composable -actual fun DownloadImageArrow(modifier: Modifier){ +actual fun DownloadImageArrow(modifier: Modifier) { Image( vectorXmlResource("drawable/ic_arrow.xml"), "Download", @@ -89,33 +94,32 @@ actual fun DownloadImageArrow(modifier: Modifier){ } @Composable -actual fun DownloadAllImage():ImageVector = vectorXmlResource("drawable/ic_download_arrow.xml") +actual fun DownloadAllImage(): ImageVector = vectorXmlResource("drawable/ic_download_arrow.xml") @Composable -actual fun ShareImage():ImageVector = vectorXmlResource("drawable/ic_share_open.xml") +actual fun ShareImage(): ImageVector = vectorXmlResource("drawable/ic_share_open.xml") @Composable -actual fun PlaceHolderImage():ImageVector = vectorXmlResource("drawable/music.xml") - +actual fun PlaceHolderImage(): ImageVector = vectorXmlResource("drawable/music.xml") @Composable -actual fun SpotiFlyerLogo():ImageVector = +actual fun SpotiFlyerLogo(): ImageVector = vectorXmlResource("drawable/ic_spotiflyer_logo.xml") @Composable -actual fun HeartIcon():ImageVector = vectorXmlResource("drawable/ic_heart.xml") +actual fun HeartIcon(): ImageVector = vectorXmlResource("drawable/ic_heart.xml") @Composable -actual fun SpotifyLogo():ImageVector = vectorXmlResource("drawable/ic_spotify_logo.xml") +actual fun SpotifyLogo(): ImageVector = vectorXmlResource("drawable/ic_spotify_logo.xml") @Composable -actual fun YoutubeLogo():ImageVector = vectorXmlResource("drawable/ic_youtube.xml") +actual fun YoutubeLogo(): ImageVector = vectorXmlResource("drawable/ic_youtube.xml") @Composable -actual fun GaanaLogo():ImageVector = vectorXmlResource("drawable/ic_gaana.xml") +actual fun GaanaLogo(): ImageVector = vectorXmlResource("drawable/ic_gaana.xml") @Composable -actual fun YoutubeMusicLogo():ImageVector = vectorXmlResource("drawable/ic_youtube_music_logo.xml") +actual fun YoutubeMusicLogo(): ImageVector = vectorXmlResource("drawable/ic_youtube_music_logo.xml") @Composable -actual fun GithubLogo():ImageVector = vectorXmlResource("drawable/ic_github.xml") +actual fun GithubLogo(): ImageVector = vectorXmlResource("drawable/ic_github.xml") diff --git a/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopToast.kt b/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopToast.kt index c1be0e0f..dea14703 100644 --- a/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopToast.kt +++ b/common/compose/src/desktopMain/kotlin/com/shabinder/common/uikit/DesktopToast.kt @@ -78,9 +78,9 @@ actual fun Toast( isShown = false visibility.value = false } - onDispose { } + onDispose { } } } } } -} \ No newline at end of file +} diff --git a/common/data-models/build.gradle.kts b/common/data-models/build.gradle.kts index 882e4f0a..e44de175 100644 --- a/common/data-models/build.gradle.kts +++ b/common/data-models/build.gradle.kts @@ -31,4 +31,4 @@ kotlin { } } } -} \ No newline at end of file +} diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt index b11c56de..14f894f1 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/AllPlatforms.kt @@ -16,8 +16,8 @@ package com.shabinder.common.models -sealed class AllPlatforms{ - object Js:AllPlatforms() - object Jvm:AllPlatforms() - object Native:AllPlatforms() +sealed class AllPlatforms { + object Js : AllPlatforms() + object Jvm : AllPlatforms() + object Native : AllPlatforms() } diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt index 079dd2bb..8a6c0e0a 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/CorsProxy.kt @@ -16,24 +16,24 @@ package com.shabinder.common.models -sealed class CorsProxy(open val url: String){ - data class SelfHostedCorsProxy(override val url:String = "https://kind-grasshopper-73.telebit.io/cors/"):CorsProxy(url) - data class PublicProxyWithExtension(override val url:String = "https://cors.bridged.cc/"):CorsProxy(url) +sealed class CorsProxy(open val url: String) { + data class SelfHostedCorsProxy(override val url: String = "https://kind-grasshopper-73.telebit.io/cors/") : CorsProxy(url) + data class PublicProxyWithExtension(override val url: String = "https://cors.bridged.cc/") : CorsProxy(url) - fun toggle(mode:CorsProxy? = null):CorsProxy{ + fun toggle(mode: CorsProxy? = null): CorsProxy { mode?.let { corsProxy = mode return corsProxy } - corsProxy = when(corsProxy){ + corsProxy = when (corsProxy) { is SelfHostedCorsProxy -> PublicProxyWithExtension() is PublicProxyWithExtension -> SelfHostedCorsProxy() } return corsProxy } - fun extensionMode():Boolean{ - return when(corsProxy){ + fun extensionMode(): Boolean { + return when (corsProxy) { is SelfHostedCorsProxy -> false is PublicProxyWithExtension -> true } @@ -44,4 +44,4 @@ sealed class CorsProxy(open val url: String){ * This Var Keeps Track for Cors Config in JS Platform * Default Self Hosted, However ask user to use extension if possible. * */ -var corsProxy:CorsProxy = CorsProxy.SelfHostedCorsProxy() +var corsProxy: CorsProxy = CorsProxy.SelfHostedCorsProxy() diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt index b3aae462..7d004ed6 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadObject.kt @@ -24,30 +24,29 @@ import kotlinx.serialization.Serializable @Parcelize @Serializable data class TrackDetails( - var title:String, - var artists:List, - var durationSec:Int, - var albumName:String?=null, - var year:String?=null, - var comment:String?=null, - var lyrics:String?=null, - var trackUrl:String?=null, + var title: String, + var artists: List, + var durationSec: Int, + var albumName: String? = null, + var year: String? = null, + var comment: String? = null, + var lyrics: String? = null, + var trackUrl: String? = null, var albumArtPath: String, var albumArtURL: String, var source: Source, val progress: Int = 2, val downloaded: DownloadStatus = DownloadStatus.NotDownloaded, var outputFilePath: String, - var videoID:String? = null, -):Parcelable - + var videoID: String? = null, +) : Parcelable @Serializable -sealed class DownloadStatus:Parcelable { - @Parcelize object Downloaded :DownloadStatus() - @Parcelize data class Downloading(val progress: Int = 2):DownloadStatus() - @Parcelize object Queued :DownloadStatus() - @Parcelize object NotDownloaded :DownloadStatus() - @Parcelize object Converting :DownloadStatus() - @Parcelize object Failed :DownloadStatus() +sealed class DownloadStatus : Parcelable { + @Parcelize object Downloaded : DownloadStatus() + @Parcelize data class Downloading(val progress: Int = 2) : DownloadStatus() + @Parcelize object Queued : DownloadStatus() + @Parcelize object NotDownloaded : DownloadStatus() + @Parcelize object Converting : DownloadStatus() + @Parcelize object Failed : DownloadStatus() } diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadRecord.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadRecord.kt index f90001be..0c8cf7bb 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadRecord.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadRecord.kt @@ -17,10 +17,10 @@ package com.shabinder.common.models data class DownloadRecord( - var id:Long = 0, - var type:String, - var name:String, - var link:String, - var coverUrl:String, - var totalFiles:Long = 1, -) \ No newline at end of file + var id: Long = 0, + var type: String, + var name: String, + var link: String, + var coverUrl: String, + var totalFiles: Long = 1, +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadResult.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadResult.kt index 9fb22d58..9d5d26f3 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadResult.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/DownloadResult.kt @@ -20,7 +20,7 @@ sealed class DownloadResult { data class Error(val message: String, val cause: Exception? = null) : DownloadResult() - data class Progress(val progress: Int): DownloadResult() + data class Progress(val progress: Int) : DownloadResult() data class Success(val byteArray: ByteArray) : DownloadResult() { override fun equals(other: Any?): Boolean { @@ -38,4 +38,4 @@ sealed class DownloadResult { return byteArray.contentHashCode() } } -} \ No newline at end of file +} diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/Optional.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/Optional.kt index a8e960de..3b24765e 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/Optional.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/Optional.kt @@ -19,4 +19,4 @@ package com.shabinder.common.models import kotlinx.serialization.Serializable @Serializable -data class Optional(val value: T?) \ No newline at end of file +data class Optional(val value: T?) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/PlatformQueryResult.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/PlatformQueryResult.kt index 04c39366..ceeeed63 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/PlatformQueryResult.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/PlatformQueryResult.kt @@ -27,4 +27,4 @@ data class PlatformQueryResult( var coverUrl: String, var trackList: List, var source: Source -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/YoutubeTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/YoutubeTrack.kt index 87ba0762..dc1abc90 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/YoutubeTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/YoutubeTrack.kt @@ -21,8 +21,8 @@ import kotlinx.serialization.Serializable @Serializable data class YoutubeTrack( var name: String? = null, - var type: String? = null, // Song / Video + var type: String? = null, // Song / Video var artist: String? = null, - var duration:String? = null, + var duration: String? = null, var videoId: String? = null -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Artist.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Artist.kt index 2e8cadab..fba24e20 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Artist.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Artist.kt @@ -20,9 +20,9 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class Artist ( - val popularity : Int, - val seokey : String, - val name : String, - @SerialName("artwork_175x175")var artworkLink :String? = null -) \ No newline at end of file +data class Artist( + val popularity: Int, + val seokey: String, + val name: String, + @SerialName("artwork_175x175")var artworkLink: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/CustomArtworks.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/CustomArtworks.kt index d6718ce8..76ffabdc 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/CustomArtworks.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/CustomArtworks.kt @@ -17,14 +17,13 @@ package com.shabinder.common.models.gaana import kotlinx.serialization.SerialName - import kotlinx.serialization.Serializable @Serializable -data class CustomArtworks ( - @SerialName("40x40") val size_40p : String, - @SerialName("80x80") val size_80p : String, - @SerialName("110x110")val size_110p : String, - @SerialName("175x175")val size_175p : String, - @SerialName("480x480")val size_480p : String, -) \ No newline at end of file +data class CustomArtworks( + @SerialName("40x40") val size_40p: String, + @SerialName("80x80") val size_80p: String, + @SerialName("110x110")val size_110p: String, + @SerialName("175x175")val size_175p: String, + @SerialName("480x480")val size_480p: String, +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt index cb01d110..b050acf9 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt @@ -19,10 +19,10 @@ package com.shabinder.common.models.gaana import kotlinx.serialization.Serializable @Serializable -data class GaanaAlbum ( - val tracks : List, - val count : Int, - val custom_artworks : CustomArtworks, - val release_year : Int, - val favorite_count : Int, -) \ No newline at end of file +data class GaanaAlbum( + val tracks: List, + val count: Int, + val custom_artworks: CustomArtworks, + val release_year: Int, + val favorite_count: Int, +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistDetails.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistDetails.kt index 82093577..c8d8271d 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistDetails.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistDetails.kt @@ -20,6 +20,6 @@ import kotlinx.serialization.Serializable @Serializable data class GaanaArtistDetails( - val artist : List, - val count : Int, -) \ No newline at end of file + val artist: List, + val count: Int, +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistTracks.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistTracks.kt index 75cc908d..e54a5a96 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistTracks.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaArtistTracks.kt @@ -20,6 +20,6 @@ import kotlinx.serialization.Serializable @Serializable data class GaanaArtistTracks( - val count : Int, - val tracks : List? = null -) \ No newline at end of file + val count: Int, + val tracks: List? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaPlaylist.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaPlaylist.kt index b8b96a12..94f6032c 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaPlaylist.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaPlaylist.kt @@ -19,10 +19,10 @@ package com.shabinder.common.models.gaana import kotlinx.serialization.Serializable @Serializable -data class GaanaPlaylist ( - val modified_on : String, - val count : Int, - val created_on : String, - val favorite_count : Int, - val tracks : List, -) \ No newline at end of file +data class GaanaPlaylist( + val modified_on: String, + val count: Int, + val created_on: String, + val favorite_count: Int, + val tracks: List, +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaSong.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaSong.kt index 39d0a9c0..d421b7b6 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaSong.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaSong.kt @@ -20,5 +20,5 @@ import kotlinx.serialization.Serializable @Serializable data class GaanaSong( - val tracks : List -) \ No newline at end of file + val tracks: List +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaTrack.kt index e687b5ac..ece6bd99 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaTrack.kt @@ -21,22 +21,22 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class GaanaTrack ( - val tags : List? = null, - val seokey : String, - val albumseokey : String? = null, - val track_title : String, - val album_title : String? = null, - val language : String? = null, +data class GaanaTrack( + val tags: List? = null, + val seokey: String, + val albumseokey: String? = null, + val track_title: String, + val album_title: String? = null, + val language: String? = null, val duration: Int, - @SerialName("artwork_large") val artworkLink : String, - val artist : List = emptyList(), - @SerialName("gener") val genre : List? = null, - val lyrics_url : String? = null, - val youtube_id : String? = null, - val total_favourite_count : Int? = null, - val release_date : String? = null, - val play_ct : String? = null, - val secondary_language : String? = null, + @SerialName("artwork_large") val artworkLink: String, + val artist: List = emptyList(), + @SerialName("gener") val genre: List? = null, + val lyrics_url: String? = null, + val youtube_id: String? = null, + val total_favourite_count: Int? = null, + val release_date: String? = null, + val play_ct: String? = null, + val secondary_language: String? = null, var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Genre.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Genre.kt index bc17dc04..ea8a6fdd 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Genre.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Genre.kt @@ -19,7 +19,7 @@ package com.shabinder.common.models.gaana import kotlinx.serialization.Serializable @Serializable -data class Genre ( - val genre_id : Int, - val name : String -) \ No newline at end of file +data class Genre( + val genre_id: Int, + val name: String +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Tags.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Tags.kt index bc550cbe..5852c335 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Tags.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/Tags.kt @@ -19,7 +19,7 @@ package com.shabinder.common.models.gaana import kotlinx.serialization.Serializable @Serializable -data class Tags ( - val tag_id : Int, - val tag_name : String -) \ No newline at end of file +data class Tags( + val tag_id: Int, + val tag_name: String +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Album.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Album.kt index d8474cb5..dbe59c6f 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Album.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Album.kt @@ -30,11 +30,12 @@ data class Album( var href: String? = null, var id: String? = null, var images: List? = null, - var label :String? = null, + var label: String? = null, var name: String? = null, var popularity: Int? = null, var release_date: String? = null, var release_date_precision: String? = null, var tracks: PagingObjectTrack? = null, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Artist.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Artist.kt index 70153fa5..8d2e3548 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Artist.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Artist.kt @@ -25,4 +25,5 @@ data class Artist( var id: String? = null, var name: String? = null, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Copyright.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Copyright.kt index 5e592beb..da53182b 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Copyright.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Copyright.kt @@ -21,4 +21,5 @@ import kotlinx.serialization.Serializable @Serializable data class Copyright( var text: String? = null, - var type: String? = null) \ No newline at end of file + var type: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Episodes.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Episodes.kt index 7adb0384..94f85dd0 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Episodes.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Episodes.kt @@ -20,21 +20,21 @@ import kotlinx.serialization.Serializable @Serializable data class Episodes( - var audio_preview_url:String?, - var description:String?, - var duration_ms:Int?, - var explicit:Boolean?, - var external_urls:Map?, - var href:String?, - var id:String?, - var images:List?, - var is_externally_hosted:Boolean?, - var is_playable:Boolean?, - var language:String?, - var languages:List?, - var name:String?, - var release_date:String?, - var release_date_precision:String?, - var type:String?, - var uri:String -) \ No newline at end of file + var audio_preview_url: String?, + var description: String?, + var duration_ms: Int?, + var explicit: Boolean?, + var external_urls: Map?, + var href: String?, + var id: String?, + var images: List?, + var is_externally_hosted: Boolean?, + var is_playable: Boolean?, + var language: String?, + var languages: List?, + var name: String?, + var release_date: String?, + var release_date_precision: String?, + var type: String?, + var uri: String +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Followers.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Followers.kt index 312b9c33..254c26fc 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Followers.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Followers.kt @@ -21,4 +21,5 @@ import kotlinx.serialization.Serializable @Serializable data class Followers( var href: String? = null, - var total: Int? = null) \ No newline at end of file + var total: Int? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Image.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Image.kt index 5e5e5138..692e5d45 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Image.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Image.kt @@ -22,4 +22,5 @@ import kotlinx.serialization.Serializable data class Image( var width: Int? = null, var height: Int? = null, - var url: String? = null) \ No newline at end of file + var url: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/LinkedTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/LinkedTrack.kt index d73d5061..8e381ef8 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/LinkedTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/LinkedTrack.kt @@ -24,4 +24,5 @@ data class LinkedTrack( var href: String? = null, var id: String? = null, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectPlaylistTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectPlaylistTrack.kt index 8715d028..71add0eb 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectPlaylistTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectPlaylistTrack.kt @@ -26,4 +26,5 @@ data class PagingObjectPlaylistTrack( var next: String? = null, var offset: Int = 0, var previous: String? = null, - var total: Int = 0) \ No newline at end of file + var total: Int = 0 +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectTrack.kt index 81cb29eb..49a0e982 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PagingObjectTrack.kt @@ -26,4 +26,5 @@ data class PagingObjectTrack( var next: String? = null, var offset: Int = 0, var previous: String? = null, - var total: Int = 0) \ No newline at end of file + var total: Int = 0 +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Playlist.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Playlist.kt index 95769656..851e7144 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Playlist.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Playlist.kt @@ -34,4 +34,5 @@ data class Playlist( var snapshot_id: String? = null, var tracks: PagingObjectPlaylistTrack? = null, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PlaylistTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PlaylistTrack.kt index d09cc7de..e0c2cda1 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PlaylistTrack.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/PlaylistTrack.kt @@ -23,4 +23,5 @@ data class PlaylistTrack( var added_at: String? = null, var added_by: UserPublic? = null, var track: Track? = null, - var is_local: Boolean? = null) \ No newline at end of file + var is_local: Boolean? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Source.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Source.kt index 2fed43c3..b7e99c6a 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Source.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Source.kt @@ -20,4 +20,4 @@ enum class Source { Spotify, YouTube, Gaana, -} \ No newline at end of file +} diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/TokenData.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/TokenData.kt index 5ffee873..41d82a2c 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/TokenData.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/TokenData.kt @@ -21,7 +21,7 @@ import kotlinx.serialization.Serializable @Serializable data class TokenData( - var access_token:String?, - var token_type:String?, - @SerialName("expires_in") var expiry:Long? -) \ No newline at end of file + var access_token: String?, + var token_type: String?, + @SerialName("expires_in") var expiry: Long? +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Track.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Track.kt index 72799110..83a87ae9 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Track.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/Track.kt @@ -40,4 +40,3 @@ data class Track( var popularity: Int? = null, var downloaded: DownloadStatus = DownloadStatus.NotDownloaded ) - diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPrivate.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPrivate.kt index b2300fe1..b3a2e8b5 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPrivate.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPrivate.kt @@ -20,14 +20,15 @@ import kotlinx.serialization.Serializable @Serializable data class UserPrivate( - val country:String, + val country: String, var display_name: String, - val email:String, + val email: String, var external_urls: Map? = null, var followers: Followers? = null, var href: String? = null, var id: String? = null, var images: List? = null, - var product:String, + var product: String, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPublic.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPublic.kt index 0321c41b..5addc5d9 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPublic.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/spotify/UserPublic.kt @@ -27,4 +27,5 @@ data class UserPublic( var id: String? = null, var images: List? = null, var type: String? = null, - var uri: String? = null) \ No newline at end of file + var uri: String? = null +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/AlbumRefWynk.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/AlbumRefWynk.kt index 279dbdd8..679bb3b6 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/AlbumRefWynk.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/AlbumRefWynk.kt @@ -22,4 +22,4 @@ data class AlbumRefWynk( val smallImage: String, val title: String, val type: String -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/HtDataWynk.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/HtDataWynk.kt index 932ba640..38d292c4 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/HtDataWynk.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/HtDataWynk.kt @@ -20,4 +20,4 @@ data class HtDataWynk( val cutName: String, val previewUrl: String, val vcode: String -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ItemWynk.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ItemWynk.kt index f0774f79..b28ff434 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ItemWynk.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ItemWynk.kt @@ -27,7 +27,7 @@ data class ItemWynk( val cues: List, val downloadPrice: String, val downloadUrl: String, - val duration: Int, //in Seconds + val duration: Int, // in Seconds val exclusive: Boolean, val formats: List, val htData: List, @@ -42,11 +42,11 @@ data class ItemWynk( val rentUrl: String, val serverEtag: String, val shortUrl: String, - val smallImage: String, //Cover Image after Replacing 120x120 with 720x720 + val smallImage: String, // Cover Image after Replacing 120x120 with 720x720 val subtitle: String, // String : `ArtistName - TrackName` - val subtitleId: String, //ARTIST NAME,artist-id , etc //USE SUBTITLE INSTEAD + val subtitleId: String, // ARTIST NAME,artist-id , etc //USE SUBTITLE INSTEAD val subtitleType: String, // ARTIST etc val title: String, - val type: String, //Song ,etc + val type: String, // Song ,etc val videoPresent: Boolean -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ShortURLWynk.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ShortURLWynk.kt index 99506322..66f90c0e 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ShortURLWynk.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/ShortURLWynk.kt @@ -16,8 +16,6 @@ package com.shabinder.common.models.wynk - - // Use Kotlinx JSON Parsing as in YT Music data class ShortURLWynk( val actualTotal: Int, @@ -33,15 +31,15 @@ data class ShortURLWynk( val isFollowable: Boolean, val isHt: Boolean, val itemIds: List, - val itemTypes: List, //Songs , etc + val itemTypes: List, // Songs , etc val items: List, val lang: String, - val largeImage: String, //Cover Image Alternate + val largeImage: String, // Cover Image Alternate val lastUpdated: Long, val offset: Int, val owner: String, val playIcon: Boolean, - val playlistImage: String, //Cover Image + val playlistImage: String, // Cover Image val redesignFeaturedImage: String, val shortUrl: String, val singers: List, @@ -49,4 +47,4 @@ data class ShortURLWynk( val title: String, val total: Int, val type: String -) \ No newline at end of file +) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/SingerWynk.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/SingerWynk.kt index c5f1b473..a5f91408 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/SingerWynk.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/wynk/SingerWynk.kt @@ -23,4 +23,4 @@ data class SingerWynk( val smallImage: String, val title: String, val type: String -) \ No newline at end of file +) diff --git a/common/database/build.gradle.kts b/common/database/build.gradle.kts index 787d25f0..d29f34f2 100644 --- a/common/database/build.gradle.kts +++ b/common/database/build.gradle.kts @@ -31,7 +31,7 @@ kotlin { commonMain { dependencies { implementation(project(":common:data-models")) - //implementation(Badoo.Reaktive.reaktive) + // implementation(Badoo.Reaktive.reaktive) // SQL Delight implementation(SqlDelight.runtime) implementation(SqlDelight.coroutineExtensions) diff --git a/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt b/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt index 91515e53..72ff26e7 100644 --- a/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt +++ b/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt @@ -36,4 +36,4 @@ actual fun createDatabase(): Database? { val driver = AndroidSqliteDriver(Database.Schema, appContext, "Database.db") return Database(driver) } -actual fun getLogger(): Logger = LogcatLogger() \ No newline at end of file +actual fun getLogger(): Logger = LogcatLogger() diff --git a/common/database/src/commonMain/kotlin/com/shabinder/common/database/Expect.kt b/common/database/src/commonMain/kotlin/com/shabinder/common/database/Expect.kt index 1efdbc7c..20d36185 100644 --- a/common/database/src/commonMain/kotlin/com/shabinder/common/database/Expect.kt +++ b/common/database/src/commonMain/kotlin/com/shabinder/common/database/Expect.kt @@ -19,5 +19,5 @@ package com.shabinder.common.database import co.touchlab.kermit.Logger import com.shabinder.database.Database -expect fun createDatabase() : Database? -expect fun getLogger(): Logger \ No newline at end of file +expect fun createDatabase(): Database? +expect fun getLogger(): Logger diff --git a/common/database/src/desktopMain/kotlin/com/shabinder/common/database/Actual.kt b/common/database/src/desktopMain/kotlin/com/shabinder/common/database/Actual.kt index 7bf40007..df6200aa 100644 --- a/common/database/src/desktopMain/kotlin/com/shabinder/common/database/Actual.kt +++ b/common/database/src/desktopMain/kotlin/com/shabinder/common/database/Actual.kt @@ -29,4 +29,4 @@ actual fun createDatabase(): Database? { .also { Database.Schema.create(it) } return Database(driver) } -actual fun getLogger(): Logger = CommonLogger() \ No newline at end of file +actual fun getLogger(): Logger = CommonLogger() diff --git a/common/database/src/jsMain/kotlin/com/shabinder/common/database/Actual.kt b/common/database/src/jsMain/kotlin/com/shabinder/common/database/Actual.kt index 87492a88..35871b53 100644 --- a/common/database/src/jsMain/kotlin/com/shabinder/common/database/Actual.kt +++ b/common/database/src/jsMain/kotlin/com/shabinder/common/database/Actual.kt @@ -21,4 +21,4 @@ import co.touchlab.kermit.Logger import com.shabinder.database.Database actual fun createDatabase(): Database? = null -actual fun getLogger(): Logger = CommonLogger() \ No newline at end of file +actual fun getLogger(): Logger = CommonLogger() diff --git a/common/dependency-injection/build.gradle.kts b/common/dependency-injection/build.gradle.kts index f6223bf9..b3b2de94 100644 --- a/common/dependency-injection/build.gradle.kts +++ b/common/dependency-injection/build.gradle.kts @@ -44,7 +44,7 @@ kotlin { } } androidMain { - dependencies{ + dependencies { implementation(compose.materialIconsExtended) implementation(Koin.android) implementation(Ktor.clientAndroid) @@ -52,11 +52,11 @@ kotlin { implementation(Extras.Android.razorpay) api(Extras.youtubeDownloader) api(Extras.mp3agic) - //api(files("$rootDir/libs/mobile-ffmpeg.aar")) + // api(files("$rootDir/libs/mobile-ffmpeg.aar")) } } desktopMain { - dependencies{ + dependencies { implementation(compose.materialIconsExtended) implementation(Ktor.clientApache) implementation(Ktor.slf4j) @@ -68,9 +68,9 @@ kotlin { dependencies { implementation(project(":common:data-models")) implementation(Ktor.clientJs) - implementation(npm("browser-id3-writer","4.4.0")) - implementation(npm("file-saver","2.0.4")) - //implementation(npm("@types/file-saver","2.0.1",generateExternals = true)) + implementation(npm("browser-id3-writer", "4.4.0")) + implementation(npm("file-saver", "2.0.4")) + // implementation(npm("@types/file-saver","2.0.1",generateExternals = true)) } } } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt index ee1966db..3ec239c7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt @@ -32,7 +32,7 @@ import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.Dispatchers import org.json.JSONObject -actual fun openPlatform(packageID:String, platformLink:String){ +actual fun openPlatform(packageID: String, platformLink: String) { val manager: PackageManager = activityContext.packageManager try { val intent = manager.getLaunchIntentForPackage(packageID) @@ -49,10 +49,10 @@ actual fun openPlatform(packageID:String, platformLink:String){ actual val dispatcherIO = Dispatchers.IO actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm -actual val isInternetAvailable:Boolean +actual val isInternetAvailable: Boolean get() = internetAvailability.value ?: true -actual fun shareApp(){ +actual fun shareApp() { val sendIntent: Intent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, "Hey, checkout this excellent Music Downloader http://github.com/Shabinder/SpotiFlyer") @@ -78,18 +78,18 @@ private fun startPayment(mainActivity: Activity = activityContext as Activity) { val preFill = JSONObject() val options = JSONObject().apply { - put("name","SpotiFlyer") - put("description","Thanks For the Donation!") - //You can omit the image option to fetch the image from dashboard - //put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png") - put("currency","INR") - put("amount","4900") - put("prefill",preFill) + put("name", "SpotiFlyer") + put("description", "Thanks For the Donation!") + // You can omit the image option to fetch the image from dashboard + // put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png") + put("currency", "INR") + put("amount", "4900") + put("prefill", preFill) } - co.open(mainActivity,options) - }catch (e: Exception){ - //showPop("Error in payment: "+ e.message) + co.open(mainActivity, options) + } catch (e: Exception) { + // showPop("Error in payment: "+ e.message) e.printStackTrace() } } @@ -104,15 +104,15 @@ actual suspend fun downloadTracks( list: List, fetcher: FetchPlatformQueryResult, dir: Dir -){ - if(!list.isNullOrEmpty()){ +) { + if (!list.isNullOrEmpty()) { val serviceIntent = Intent(activityContext, ForegroundService::class.java) - serviceIntent.putParcelableArrayListExtra("object",ArrayList(list)) + serviceIntent.putParcelableArrayListExtra("object", ArrayList(list)) activityContext.let { ContextCompat.startForegroundService(it, serviceIntent) } } } -fun YoutubeVideo.getData(): Format?{ +fun YoutubeVideo.getData(): Format? { return try { findAudioWithQuality(AudioQuality.medium)?.get(0) as Format } catch (e: java.lang.IndexOutOfBoundsException) { @@ -126,4 +126,4 @@ fun YoutubeVideo.getData(): Format?{ } } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt index c26a2735..492355c4 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt @@ -21,7 +21,6 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.media.MediaScannerConnection import android.os.Environment -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap import co.touchlab.kermit.Kermit import com.mpatric.mp3agic.Mp3File @@ -55,33 +54,28 @@ actual class Dir actual constructor( @Suppress("DEPRECATION") actual fun defaultDir(): String = Environment.getExternalStorageDirectory().toString() + File.separator + - Environment.DIRECTORY_MUSIC + File.separator + - "SpotiFlyer"+ File.separator + Environment.DIRECTORY_MUSIC + File.separator + + "SpotiFlyer" + File.separator actual fun isPresent(path: String): Boolean = File(path).exists() actual fun createDirectory(dirPath: String) { val yourAppDir = File(dirPath) - if(!yourAppDir.exists() && !yourAppDir.isDirectory) - { // create empty directory - if (yourAppDir.mkdirs()) - {logger.i{"$dirPath created"}} - else - { - logger.e{"Unable to create Dir: $dirPath!"} + if (!yourAppDir.exists() && !yourAppDir.isDirectory) { // create empty directory + if (yourAppDir.mkdirs()) { logger.i { "$dirPath created" } } else { + logger.e { "Unable to create Dir: $dirPath!" } } - } - else { + } else { logger.i { "$dirPath already exists" } } } - actual suspend fun clearCache(){ + actual suspend fun clearCache() { File(imageCacheDir()).deleteRecursively() } - actual suspend fun cacheImage(image: Any,path:String) { + actual suspend fun cacheImage(image: Any, path: String) { try { FileOutputStream(path).use { out -> (image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out) @@ -100,9 +94,9 @@ actual class Dir actual constructor( /* * Check , if Fetch was Used, File is saved Already, else write byteArray we Received * */ - //if(!m4aFile.exists()) m4aFile.writeBytes(mp3ByteArray) + // if(!m4aFile.exists()) m4aFile.writeBytes(mp3ByteArray) - when(trackDetails.outputFilePath.substringAfterLast('.')){ + when (trackDetails.outputFilePath.substringAfterLast('.')) { ".mp3" -> { Mp3File(File(songFile.absolutePath)) .removeAllTags() @@ -136,22 +130,23 @@ actual class Dir actual constructor( }*/ } else -> { - try{ + try { Mp3File(File(songFile.absolutePath)) .removeAllTags() .setId3v1Tags(trackDetails) .setId3v2TagsAndSaveFile(trackDetails) addToLibrary(songFile.absolutePath) - }catch (e:Exception){e.printStackTrace()} + } catch (e: Exception) { e.printStackTrace() } } } } - actual fun addToLibrary(path:String) { - logger.d{"Scanning File"} + actual fun addToLibrary(path: String) { + logger.d { "Scanning File" } MediaScannerConnection.scanFile( appContext, - listOf(path).toTypedArray(), null,null) + listOf(path).toTypedArray(), null, null + ) } actual suspend fun loadImage(url: String): Picture { @@ -167,7 +162,7 @@ actual class Dir actual constructor( null } } - private suspend fun freshImage(url:String): Bitmap?{ + private suspend fun freshImage(url: String): Bitmap? { return try { val source = URL(url) val connection: HttpURLConnection = source.openConnection() as HttpURLConnection @@ -179,7 +174,7 @@ actual class Dir actual constructor( if (result != null) { GlobalScope.launch(Dispatchers.IO) { - cacheImage(result,imageCacheDir() + getNameURL(url)) + cacheImage(result, imageCacheDir() + getNameURL(url)) } result } else null @@ -190,4 +185,4 @@ actual class Dir actual constructor( } actual val db: Database? = database -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidNetworkObserver.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidNetworkObserver.kt index 815b0033..43935ab7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidNetworkObserver.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidNetworkObserver.kt @@ -24,7 +24,6 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET import android.net.NetworkRequest import android.util.Log import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.runtime.State import androidx.lifecycle.LiveData import com.shabinder.common.database.appContext @@ -41,8 +40,8 @@ const val TAG = "C-Manager" val internetAvailability by lazy { ConnectionLiveData(appContext) } @Composable -fun isInternetAvailableState(): State{ - return internetAvailability.observeAsState() +fun isInternetAvailableState(): State { + return internetAvailability.observeAsState() } /** @@ -83,17 +82,17 @@ class ConnectionLiveData(context: Context = appContext) : LiveData() { Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network) */ override fun onAvailable(network: Network) { - Log.d(TAG, "onAvailable: ${network}") + Log.d(TAG, "onAvailable: $network") val networkCapabilities = cm.getNetworkCapabilities(network) val hasInternetCapability = networkCapabilities?.hasCapability(NET_CAPABILITY_INTERNET) - Log.d(TAG, "onAvailable: ${network}, $hasInternetCapability") + Log.d(TAG, "onAvailable: $network, $hasInternetCapability") if (hasInternetCapability == true) { // check if this network actually has internet CoroutineScope(Dispatchers.IO).launch { val hasInternet = DoesNetworkHaveInternet.execute(network.socketFactory) - if(hasInternet){ - withContext(Dispatchers.Main){ - Log.d(TAG, "onAvailable: adding network. ${network}") + if (hasInternet) { + withContext(Dispatchers.Main) { + Log.d(TAG, "onAvailable: adding network. $network") validNetworks.add(network) checkValidNetworks() } @@ -107,11 +106,10 @@ class ConnectionLiveData(context: Context = appContext) : LiveData() { Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onLost(android.net.Network) */ override fun onLost(network: Network) { - Log.d(TAG, "onLost: ${network}") + Log.d(TAG, "onLost: $network") validNetworks.remove(network) checkValidNetworks() } - } /** @@ -122,17 +120,17 @@ class ConnectionLiveData(context: Context = appContext) : LiveData() { // Make sure to execute this on a background thread. fun execute(socketFactory: SocketFactory): Boolean { - return try{ + return try { Log.d(TAG, "PINGING google.") val socket = socketFactory.createSocket() ?: throw IOException("Socket is null.") socket.connect(InetSocketAddress("8.8.8.8", 53), 1500) socket.close() Log.d(TAG, "PING success.") true - }catch (e: IOException){ + } catch (e: IOException) { Log.e(TAG, "No internet connection. $e") false } } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidPicture.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidPicture.kt index 5db54e38..35b485a7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidPicture.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidPicture.kt @@ -18,6 +18,6 @@ package com.shabinder.common.di import androidx.compose.ui.graphics.ImageBitmap -actual data class Picture ( +actual data class Picture( var image: ImageBitmap? -) \ No newline at end of file +) diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/ID3Tagging.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/ID3Tagging.kt index 17ea7e4d..89cea5c7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/ID3Tagging.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/ID3Tagging.kt @@ -48,7 +48,7 @@ fun Mp3File.setId3v1Tags(track: TrackDetails): Mp3File { } @Suppress("BlockingMethodInNonBlockingContext") -suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails){ +suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { val id3v2Tag = ID3v24Tag().apply { artist = track.artists.joinToString(",") title = track.title @@ -58,37 +58,37 @@ suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails){ lyrics = "Gonna Implement Soon" url = track.trackUrl } - try{ + try { val art = File(track.albumArtPath) val bytesArray = ByteArray(art.length().toInt()) val fis = FileInputStream(art) - fis.read(bytesArray) //read file into bytes[] + fis.read(bytesArray) // read file into bytes[] fis.close() id3v2Tag.setAlbumImage(bytesArray, "image/jpeg") this.id3v2Tag = id3v2Tag saveFile(track.outputFilePath) - }catch (e: java.io.FileNotFoundException){ + } catch (e: java.io.FileNotFoundException) { try { - //Image Still Not Downloaded! - //Lets Download Now and Write it into Album Art + // Image Still Not Downloaded! + // Lets Download Now and Write it into Album Art downloadFile(track.albumArtURL).collect { - when(it){ - is DownloadResult.Error -> {}//Error + when (it) { + is DownloadResult.Error -> {} // Error is DownloadResult.Success -> { id3v2Tag.setAlbumImage(it.byteArray, "image/jpeg") this.id3v2Tag = id3v2Tag saveFile(track.outputFilePath) } - is DownloadResult.Progress -> {}//Nothing for Now , no progress bar to show + is DownloadResult.Progress -> {} // Nothing for Now , no progress bar to show } } - }catch (e: Exception){ - //log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}") + } catch (e: Exception) { + // log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}") } } } -fun Mp3File.saveFile(filePath: String){ +fun Mp3File.saveFile(filePath: String) { save(filePath.substringBeforeLast('.') + ".new.mp3") val m4aFile = File(filePath) m4aFile.delete() diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/LiveDataExt.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/LiveDataExt.kt index ab6ab74a..2350f57c 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/LiveDataExt.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/LiveDataExt.kt @@ -16,7 +16,11 @@ package com.shabinder.common.di -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner @@ -46,4 +50,4 @@ fun LiveData.observeAsState(initial: R): State { onDispose { removeObserver(observer) } } return state -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt index 20af782b..c026ed45 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt @@ -23,7 +23,7 @@ import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.spotify.Source -import io.ktor.client.* +import io.ktor.client.HttpClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -31,7 +31,7 @@ actual class YoutubeProvider actual constructor( private val httpClient: HttpClient, private val logger: Kermit, private val dir: Dir, -){ +) { val ytDownloader: YoutubeDownloader = YoutubeDownloader() /* * YT Album Art Schema @@ -42,38 +42,38 @@ actual class YoutubeProvider actual constructor( private val sampleDomain2 = "youtube.com" private val sampleDomain3 = "youtu.be" - actual suspend fun query(fullLink: String): PlatformQueryResult?{ + actual suspend fun query(fullLink: String): PlatformQueryResult? { val link = fullLink.removePrefix("https://").removePrefix("http://") - if(link.contains("playlist",true) || link.contains("list",true)){ + if (link.contains("playlist", true) || link.contains("list", true)) { // Given Link is of a Playlist - logger.i{ link } + logger.i { link } val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&").substringBefore("?") - return withContext(Dispatchers.IO){ + return withContext(Dispatchers.IO) { getYTPlaylist( playlistId ) } - }else{//Given Link is of a Video + } else { // Given Link is of a Video var searchId = "error" - when{ - link.contains(sampleDomain1,true) -> {//Youtube Music - searchId = link.substringAfterLast("/","error").substringBefore("&").substringAfterLast("=") + when { + link.contains(sampleDomain1, true) -> { // Youtube Music + searchId = link.substringAfterLast("/", "error").substringBefore("&").substringAfterLast("=") } - link.contains(sampleDomain2,true) -> {//Standard Youtube Link - searchId = link.substringAfterLast("=","error").substringBefore("&") + link.contains(sampleDomain2, true) -> { // Standard Youtube Link + searchId = link.substringAfterLast("=", "error").substringBefore("&") } - link.contains(sampleDomain3,true) -> {//Shortened Youtube Link - searchId = link.substringAfterLast("/","error").substringBefore("&") + link.contains(sampleDomain3, true) -> { // Shortened Youtube Link + searchId = link.substringAfterLast("/", "error").substringBefore("&") } } - return if(searchId != "error") { - withContext(Dispatchers.IO){ + return if (searchId != "error") { + withContext(Dispatchers.IO) { getYTTrack( searchId ) } - }else{ - logger.d{"Your Youtube Link is not of a Video!!"} + } else { + logger.d { "Your Youtube Link is not of a Video!!" } null } } @@ -81,7 +81,7 @@ actual class YoutubeProvider actual constructor( private suspend fun getYTPlaylist( searchId: String - ): PlatformQueryResult?{ + ): PlatformQueryResult? { val result = PlatformQueryResult( folderType = "", subFolder = "", @@ -99,7 +99,7 @@ actual class YoutubeProvider actual constructor( val videos = playlist.videos() coverUrl = "https://i.ytimg.com/vi/${ - videos.firstOrNull()?.videoId() + videos.firstOrNull()?.videoId() }/hqdefault.jpg" title = name @@ -113,11 +113,11 @@ actual class YoutubeProvider actual constructor( albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg", downloaded = if (dir.isPresent( dir.finalOutputDir( - itemName = it.title(), - type = folderType, - subFolder = subFolder, - dir.defaultDir() - ) + itemName = it.title(), + type = folderType, + subFolder = subFolder, + dir.defaultDir() + ) ) ) DownloadStatus.Downloaded @@ -130,16 +130,16 @@ actual class YoutubeProvider actual constructor( } } catch (e: Exception) { e.printStackTrace() - logger.d{"An Error Occurred While Processing!"} + logger.d { "An Error Occurred While Processing!" } } } - return if(result.title.isNotBlank()) result + return if (result.title.isNotBlank()) result else null } @Suppress("DefaultLocale") private suspend fun getYTTrack( - searchId:String, + searchId: String, ): PlatformQueryResult? { val result = PlatformQueryResult( folderType = "", @@ -148,15 +148,15 @@ actual class YoutubeProvider actual constructor( coverUrl = "", trackList = listOf(), Source.YouTube - ).apply{ + ).apply { try { - logger.i{searchId} + logger.i { searchId } val video = ytDownloader.getVideo(searchId) coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg" val detail = video?.details() val name = detail?.title()?.replace(detail.author()!!.toUpperCase(), "", true) ?: detail?.title() ?: "" - //logger.i{ detail.toString() } + // logger.i{ detail.toString() } trackList = listOf( TrackDetails( title = name, @@ -167,11 +167,11 @@ actual class YoutubeProvider actual constructor( albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg", downloaded = if (dir.isPresent( dir.finalOutputDir( - itemName = name, - type = folderType, - subFolder = subFolder, - defaultDir = dir.defaultDir() - ) + itemName = name, + type = folderType, + subFolder = subFolder, + defaultDir = dir.defaultDir() + ) ) ) DownloadStatus.Downloaded @@ -185,10 +185,10 @@ actual class YoutubeProvider actual constructor( title = name } catch (e: Exception) { e.printStackTrace() - logger.e{"An Error Occurred While Processing!,$searchId"} + logger.e { "An Error Occurred While Processing!,$searchId" } } } - return if(result.title.isNotBlank()) result + return if (result.title.isNotBlank()) result else null } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt index 637c96a5..485e9634 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt @@ -17,15 +17,22 @@ package com.shabinder.common.di.worker import android.annotation.SuppressLint -import android.app.* +import android.app.DownloadManager import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent import android.app.PendingIntent.FLAG_CANCEL_CURRENT +import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri -import android.os.* +import android.os.Build +import android.os.IBinder +import android.os.PowerManager import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat @@ -33,33 +40,43 @@ import androidx.core.net.toUri import co.touchlab.kermit.Kermit import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.model.formats.Format -import com.shabinder.common.database.R.* import com.shabinder.common.di.Dir import com.shabinder.common.di.FetchPlatformQueryResult +import com.shabinder.common.di.R import com.shabinder.common.di.getData import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails -import com.tonyodev.fetch2.* +import com.tonyodev.fetch2.Download +import com.tonyodev.fetch2.Error +import com.tonyodev.fetch2.Fetch +import com.tonyodev.fetch2.FetchListener +import com.tonyodev.fetch2.NetworkType +import com.tonyodev.fetch2.Priority +import com.tonyodev.fetch2.Request +import com.tonyodev.fetch2.Status import com.tonyodev.fetch2core.DownloadBlock -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import java.io.File -import java.util.* import kotlin.coroutines.CoroutineContext -class ForegroundService : Service(),CoroutineScope{ +class ForegroundService : Service(), CoroutineScope { private val tag: String = "Foreground Service" private val channelId = "ForegroundDownloaderService" private val notificationId = 101 - private var total = 0 //Total Downloads Requested - private var converted = 0//Total Files Converted - private var downloaded = 0//Total Files downloaded - private var failed = 0//Total Files failed + private var total = 0 // Total Downloads Requested + private var converted = 0 // Total Files Converted + private var downloaded = 0 // Total Files downloaded + private var failed = 0 // Total Files failed private val isFinished: Boolean get() = converted + failed == total private var isSingleDownload: Boolean = false - private lateinit var serviceJob :Job + private lateinit var serviceJob: Job override val coroutineContext: CoroutineContext get() = serviceJob + Dispatchers.IO @@ -67,18 +84,17 @@ class ForegroundService : Service(),CoroutineScope{ private val allTracksStatus = hashMapOf() private var wakeLock: PowerManager.WakeLock? = null private var isServiceStarted = false - private var messageList = mutableListOf("", "", "", "","") - private lateinit var cancelIntent:PendingIntent - private lateinit var downloadManager : DownloadManager + private var messageList = mutableListOf("", "", "", "", "") + private lateinit var cancelIntent: PendingIntent + private lateinit var downloadManager: DownloadManager private val fetcher: FetchPlatformQueryResult by inject() private val logger: Kermit by inject() private val fetch: Fetch by inject() private val dir: Dir by inject() - private val ytDownloader:YoutubeDownloader + private val ytDownloader: YoutubeDownloader get() = fetcher.youtubeProvider.ytDownloader - - + override fun onBind(intent: Intent): IBinder? = null @SuppressLint("UnspecifiedImmutableFlag") @@ -86,13 +102,13 @@ class ForegroundService : Service(),CoroutineScope{ super.onCreate() serviceJob = SupervisorJob() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(channelId,"Downloader Service") + createNotificationChannel(channelId, "Downloader Service") } val intent = Intent( this, ForegroundService::class.java - ).apply{action = "kill"} - cancelIntent = PendingIntent.getService (this, 0 , intent , FLAG_CANCEL_CURRENT ) + ).apply { action = "kill" } + cancelIntent = PendingIntent.getService(this, 0, intent, FLAG_CANCEL_CURRENT) downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager fetch.removeAllListeners().addListener(fetchListener) } @@ -100,16 +116,16 @@ class ForegroundService : Service(),CoroutineScope{ @SuppressLint("WakelockTimeout") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // Send a notification that service is started - Log.i(tag,"Foreground Service Started.") + Log.i(tag, "Foreground Service Started.") startForeground(notificationId, getNotification()) - intent?.let{ + intent?.let { when (it.action) { "kill" -> killService() "query" -> { val response = Intent().apply { action = "query_result" - synchronized(allTracksStatus){ + synchronized(allTracksStatus) { putExtra("tracks", allTracksStatus) } } @@ -117,9 +133,11 @@ class ForegroundService : Service(),CoroutineScope{ } } - val downloadObjects: ArrayList? = (it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList( - "object" - )) + val downloadObjects: ArrayList? = ( + it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList( + "object" + ) + ) downloadObjects?.let { list -> downloadObjects.size.let { size -> @@ -133,13 +151,13 @@ class ForegroundService : Service(),CoroutineScope{ downloadAllTracks(list) } } - //Wake locks and misc tasks from here : - return if (isServiceStarted){ - //Service Already Started + // Wake locks and misc tasks from here : + return if (isServiceStarted) { + // Service Already Started START_STICKY - } else{ + } else { isServiceStarted = true - Log.i(tag,"Starting the foreground service task") + Log.i(tag, "Starting the foreground service task") wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply { @@ -156,56 +174,55 @@ class ForegroundService : Service(),CoroutineScope{ private fun downloadAllTracks(trackList: List) { trackList.forEach { launch { - if (!it.videoID.isNullOrBlank()) {//Video ID already known! + if (!it.videoID.isNullOrBlank()) { // Video ID already known! downloadTrack(it.videoID!!, it) } else { val searchQuery = "${it.title} - ${it.artists.joinToString(",")}" - val videoID = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery,it) + val videoID = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery, it) logger.d("Service VideoID") { videoID ?: "Not Found" } if (videoID.isNullOrBlank()) { sendTrackBroadcast(Status.FAILED.name, it) failed++ updateNotification() allTracksStatus[it.title] = DownloadStatus.Failed - } else {//Found Youtube Video ID + } else { // Found Youtube Video ID downloadTrack(videoID, it) } } } } } - - private fun downloadTrack(videoID:String, track: TrackDetails){ + private fun downloadTrack(videoID: String, track: TrackDetails) { launch { try { val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID) - if (url == null){ - val audioData:Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error") + if (url == null) { + val audioData: Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error") val ytUrl: String = audioData.url() - enqueueDownload(ytUrl,track) - } else enqueueDownload(url,track) - }catch (e: Exception){ - logger.d("Service YT Error"){e.message.toString()} - sendTrackBroadcast(Status.FAILED.name,track) + enqueueDownload(ytUrl, track) + } else enqueueDownload(url, track) + } catch (e: Exception) { + logger.d("Service YT Error") { e.message.toString() } + sendTrackBroadcast(Status.FAILED.name, track) allTracksStatus[track.title] = DownloadStatus.Failed } } } - - private fun enqueueDownload(url:String,track:TrackDetails){ - val request= Request(url, track.outputFilePath).apply{ + private fun enqueueDownload(url: String, track: TrackDetails) { + val request = Request(url, track.outputFilePath).apply { priority = Priority.NORMAL networkType = NetworkType.ALL } - fetch.enqueue(request, + fetch.enqueue( + request, { request1 -> requestMap[request1] = track - logger.d(tag){"Enqueuing Download"} + logger.d(tag) { "Enqueuing Download" } }, { error -> - logger.d(tag){"Enqueuing Error:${error.throwable.toString()}"} + logger.d(tag) { "Enqueuing Error:${error.throwable}" } } ) } @@ -235,12 +252,12 @@ class ForegroundService : Service(),CoroutineScope{ totalBlocks: Int ) { launch { - val track = requestMap[download.request] + val track = requestMap[download.request] addToNotification("Downloading ${track?.title}") - logger.d(tag){"${track?.title} Download Started"} - track?.let{ + logger.d(tag) { "${track?.title} Download Started" } + track?.let { allTracksStatus[it.title] = DownloadStatus.Downloading() - sendTrackBroadcast(Status.DOWNLOADING.name,track) + sendTrackBroadcast(Status.DOWNLOADING.name, track) } } } @@ -259,25 +276,25 @@ class ForegroundService : Service(),CoroutineScope{ override fun onCompleted(download: Download) { val track = requestMap[download.request] - try{ + try { track?.let { - val job = launch { dir.saveFileWithMetadata(byteArrayOf(),it) } + val job = launch { dir.saveFileWithMetadata(byteArrayOf(), it) } allTracksStatus[it.title] = DownloadStatus.Converting - sendTrackBroadcast("Converting",it) + sendTrackBroadcast("Converting", it) addToNotification("Processing ${it.title}") job.invokeOnCompletion { _ -> converted++ allTracksStatus[it.title] = DownloadStatus.Downloaded - sendTrackBroadcast(Status.COMPLETED.name,it) + sendTrackBroadcast(Status.COMPLETED.name, it) removeFromNotification("Processing ${it.title}") } } - logger.d(tag){"${track?.title} Download Completed"} - }catch ( + logger.d(tag) { "${track?.title} Download Completed" } + } catch ( e: KotlinNullPointerException - ){ - logger.d(tag){"${track?.title} Download Failed! Error:Fetch!!!!"} - logger.d(tag){"${track?.title} Requesting Download thru Android DM"} + ) { + logger.d(tag) { "${track?.title} Download Failed! Error:Fetch!!!!" } + logger.d(tag) { "${track?.title} Requesting Download thru Android DM" } downloadUsingDM(download.request.url, download.request.file, track!!) } downloaded++ @@ -301,8 +318,8 @@ class ForegroundService : Service(),CoroutineScope{ launch { val track = requestMap[download.request] downloaded++ - logger.d(tag){download.error.throwable.toString()} - logger.d(tag){"${track?.title} Requesting Download thru Android DM"} + logger.d(tag) { download.error.throwable.toString() } + logger.d(tag) { "${track?.title} Requesting Download thru Android DM" } downloadUsingDM(download.request.url, download.request.file, track!!) requestMap.remove(download.request) removeFromNotification("Downloading ${track.title}") @@ -322,8 +339,7 @@ class ForegroundService : Service(),CoroutineScope{ launch { requestMap[download.request]?.run { allTracksStatus[title] = DownloadStatus.Downloading(download.progress) - logger.d(tag){"${title} ETA: ${etaInMilliSeconds / 1000} sec"} - + logger.d(tag) { "$title ETA: ${etaInMilliSeconds / 1000} sec" } val intent = Intent().apply { action = "Progress" @@ -337,15 +353,15 @@ class ForegroundService : Service(),CoroutineScope{ } /** - * If fetch Fails , Android Download Manager To RESCUE!! - **/ - fun downloadUsingDM(url: String, outputDir: String, track: TrackDetails){ + * If fetch Fails , Android Download Manager To RESCUE!! + **/ + fun downloadUsingDM(url: String, outputDir: String, track: TrackDetails) { launch { val uri = Uri.parse(url) val request = DownloadManager.Request(uri).apply { setAllowedNetworkTypes( DownloadManager.Request.NETWORK_WIFI or - DownloadManager.Request.NETWORK_MOBILE + DownloadManager.Request.NETWORK_MOBILE ) setAllowedOverRoaming(false) setTitle(track.title) @@ -354,19 +370,19 @@ class ForegroundService : Service(),CoroutineScope{ setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED) } - //Start Download + // Start Download val downloadID = downloadManager.enqueue(request) - logger.d("DownloadManager"){"Download Request Sent"} + logger.d("DownloadManager") { "Download Request Sent" } val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - //Fetching the download id received with the broadcast + // Fetching the download id received with the broadcast val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) - //Checking if the received broadcast is for our enqueued download by matching download id + // Checking if the received broadcast is for our enqueued download by matching download id if (downloadID == id) { allTracksStatus[track.title] = DownloadStatus.Converting - launch { dir.saveFileWithMetadata(byteArrayOf(),track);converted++ } - //Unregister this broadcast Receiver + launch { dir.saveFileWithMetadata(byteArrayOf(), track); converted++ } + // Unregister this broadcast Receiver this@ForegroundService.unregisterReceiver(this) } } @@ -375,8 +391,6 @@ class ForegroundService : Service(),CoroutineScope{ } } - - /** * This is the method that can be called to update the Notification */ @@ -387,7 +401,7 @@ class ForegroundService : Service(),CoroutineScope{ } private fun releaseWakeLock() { - logger.d(tag){"Releasing Wake Lock"} + logger.d(tag) { "Releasing Wake Lock" } try { wakeLock?.let { if (it.isHeld) { @@ -395,14 +409,14 @@ class ForegroundService : Service(),CoroutineScope{ } } } catch (e: Exception) { - logger.d(tag){"Service stopped without being started: ${e.message}"} + logger.d(tag) { "Service stopped without being started: ${e.message}" } } isServiceStarted = false } @Suppress("SameParameterValue") @RequiresApi(Build.VERSION_CODES.O) - private fun createNotificationChannel(channelId: String, channelName: String){ + private fun createNotificationChannel(channelId: String, channelName: String) { val channel = NotificationChannel( channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT @@ -416,15 +430,15 @@ class ForegroundService : Service(),CoroutineScope{ * Cleaning All Residual Files except Mp3 Files **/ private fun cleanFiles(dir: File) { - logger.d(tag){"Starting Cleaning in ${dir.path} "} + logger.d(tag) { "Starting Cleaning in ${dir.path} " } val fList = dir.listFiles() fList?.let { for (file in fList) { if (file.isDirectory) { cleanFiles(file) - } else if(file.isFile) { - if(file.path.toString().substringAfterLast(".") != "mp3"){ - logger.d(tag){ "Cleaning ${file.path}"} + } else if (file.isFile) { + if (file.path.toString().substringAfterLast(".") != "mp3") { + logger.d(tag) { "Cleaning ${file.path}" } file.delete() } } @@ -433,41 +447,41 @@ class ForegroundService : Service(),CoroutineScope{ } private fun killService() { - launch{ - logger.d(tag){"Killing Self"} - messageList = mutableListOf("Cleaning And Exiting","","","","") + launch { + logger.d(tag) { "Killing Self" } + messageList = mutableListOf("Cleaning And Exiting", "", "", "", "") fetch.cancelAll() fetch.removeAll() updateNotification() cleanFiles(File(dir.defaultDir())) - //TODO cleanFiles(File(dir.imageCacheDir())) - messageList = mutableListOf("","","","","") + // TODO cleanFiles(File(dir.imageCacheDir())) + messageList = mutableListOf("", "", "", "", "") releaseWakeLock() serviceJob.cancel() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true) } else { - stopSelf()//System will automatically close it + stopSelf() // System will automatically close it } } } override fun onDestroy() { super.onDestroy() - if(isFinished){ + if (isFinished) { killService() } } override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) - if(isFinished){ + if (isFinished) { killService() } } - private fun getNotification():Notification = NotificationCompat.Builder(this, channelId).run { - setSmallIcon(drawable.ic_download_arrow) + private fun getNotification(): Notification = NotificationCompat.Builder(this, channelId).run { + setSmallIcon(R.drawable.ic_download_arrow) setContentTitle("Total: $total Completed:$converted Failed:$failed") setSilent(true) setStyle( @@ -479,22 +493,22 @@ class ForegroundService : Service(),CoroutineScope{ addLine(messageList[messageList.size - 5]) } ) - addAction(drawable.ic_round_cancel_24,"Exit",cancelIntent) + addAction(R.drawable.ic_round_cancel_24, "Exit", cancelIntent) build() } - private fun addToNotification(message:String){ + private fun addToNotification(message: String) { messageList.add(message) updateNotification() } - private fun removeFromNotification(message: String){ + private fun removeFromNotification(message: String) { messageList.remove(message) updateNotification() } - fun sendTrackBroadcast(action:String,track:TrackDetails){ - val intent = Intent().apply{ + fun sendTrackBroadcast(action: String, track: TrackDetails) { + val intent = Intent().apply { setAction(action) putExtra("track", track) } @@ -502,9 +516,9 @@ class ForegroundService : Service(),CoroutineScope{ } } -private fun Fetch.removeAllListeners():Fetch{ +private fun Fetch.removeAllListeners(): Fetch { for (listener in this.getListenerSet()) { this.removeListener(listener) } return this -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/DI.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/DI.kt index 1a7eca55..837a054b 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/DI.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/DI.kt @@ -23,10 +23,13 @@ import com.shabinder.common.di.providers.GaanaProvider import com.shabinder.common.di.providers.SpotifyProvider import com.shabinder.common.di.providers.YoutubeMp3 import com.shabinder.common.di.providers.YoutubeMusic -import io.ktor.client.* -import io.ktor.client.features.json.* -import io.ktor.client.features.json.serializer.* -import io.ktor.client.features.logging.* +import io.ktor.client.HttpClient +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.features.json.serializer.KotlinxSerializer +import io.ktor.client.features.logging.DEFAULT +import io.ktor.client.features.logging.LogLevel +import io.ktor.client.features.logging.Logger +import io.ktor.client.features.logging.Logging import kotlinx.serialization.json.Json import org.koin.core.context.startKoin import org.koin.dsl.KoinAppDeclaration @@ -40,23 +43,25 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat fun commonModule(enableNetworkLogs: Boolean) = module { single { createHttpClient(enableNetworkLogs = enableNetworkLogs) } - single { Dir(get(),createDatabase()) } + single { Dir(get(), createDatabase()) } single { Kermit(getLogger()) } - single { TokenStore(get(),get()) } - single { YoutubeMusic(get(),get()) } - single { SpotifyProvider(get(),get(),get()) } - single { GaanaProvider(get(),get(),get()) } - single { YoutubeProvider(get(),get(),get()) } - single { YoutubeMp3(get(),get(),get()) } - single { FetchPlatformQueryResult(get(),get(),get(),get(),get(),get()) } + single { TokenStore(get(), get()) } + single { YoutubeMusic(get(), get()) } + single { SpotifyProvider(get(), get(), get()) } + single { GaanaProvider(get(), get(), get()) } + single { YoutubeProvider(get(), get(), get()) } + single { YoutubeMp3(get(), get(), get()) } + single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get()) } } -val kotlinxSerializer = KotlinxSerializer( Json { - isLenient = true - ignoreUnknownKeys = true -}) +val kotlinxSerializer = KotlinxSerializer( + Json { + isLenient = true + ignoreUnknownKeys = true + } +) -fun createHttpClient(enableNetworkLogs: Boolean = false,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient { +fun createHttpClient(enableNetworkLogs: Boolean = false, serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient { install(JsonFeature) { this.serializer = serializer } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt index 2dd32d61..0a6be2e7 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt @@ -22,9 +22,10 @@ import com.shabinder.common.di.utils.removeIllegalChars import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.TrackDetails import com.shabinder.database.Database -import io.ktor.client.request.* -import io.ktor.client.statement.* -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 kotlin.math.roundToInt @@ -33,17 +34,17 @@ expect class Dir( logger: Kermit, database: Database? = createDatabase() ) { - val db :Database? - fun isPresent(path:String):Boolean + val db: Database? + fun isPresent(path: String): Boolean fun fileSeparator(): String fun defaultDir(): String fun imageCacheDir(): String - fun createDirectory(dirPath:String) - suspend fun cacheImage(image: Any,path: String) // in Android = ImageBitmap, Desktop = BufferedImage - suspend fun loadImage(url:String): Picture + fun createDirectory(dirPath: String) + suspend fun cacheImage(image: Any, path: String) // in Android = ImageBitmap, Desktop = BufferedImage + suspend fun loadImage(url: String): Picture suspend fun clearCache() suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, trackDetails: TrackDetails) - fun addToLibrary(path:String) + fun addToLibrary(path: String) } suspend fun downloadFile(url: String): Flow { @@ -67,7 +68,7 @@ suspend fun downloadFile(url: String): Flow { } } fun getNameURL(url: String): String { - return url.substring(url.lastIndexOf('/',url.lastIndexOf('/')-1) + 1, url.length).replace('/','_') + return url.substring(url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1, url.length).replace('/', '_') } /* * Call this function at startup! @@ -80,7 +81,7 @@ fun Dir.createDirectories() { createDirectory(defaultDir() + "Playlists/") createDirectory(defaultDir() + "YT_Downloads/") } -fun Dir.finalOutputDir(itemName:String, type:String, subFolder:String, defaultDir:String, extension:String = ".mp3" ): String = +fun Dir.finalOutputDir(itemName: String, type: String, subFolder: String, defaultDir: String, extension: String = ".mp3"): String = defaultDir + removeIllegalChars(type) + this.fileSeparator() + - if(subFolder.isEmpty())"" else { removeIllegalChars(subFolder) + this.fileSeparator()} + - removeIllegalChars(itemName) + extension + if (subFolder.isEmpty())"" else { removeIllegalChars(subFolder) + this.fileSeparator() } + + removeIllegalChars(itemName) + extension diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt index 13dbe1fc..2001575d 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Expect.kt @@ -20,7 +20,7 @@ import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.CoroutineDispatcher -expect fun openPlatform(packageID:String, platformLink:String) +expect fun openPlatform(packageID: String, platformLink: String) expect fun shareApp() @@ -28,7 +28,7 @@ expect fun giveDonation() expect val dispatcherIO: CoroutineDispatcher -expect val isInternetAvailable:Boolean +expect val isInternetAvailable: Boolean expect val currentPlatform: AllPlatforms @@ -38,4 +38,4 @@ expect suspend fun downloadTracks( dir: Dir ) -expect fun queryActiveTracks() \ No newline at end of file +expect fun queryActiveTracks() diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt index e1aff479..e1fba2a5 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt @@ -22,33 +22,32 @@ import com.shabinder.common.di.providers.SpotifyProvider import com.shabinder.common.di.providers.YoutubeMp3 import com.shabinder.common.di.providers.YoutubeMusic import com.shabinder.common.models.PlatformQueryResult -import com.shabinder.database.Database import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class FetchPlatformQueryResult( private val gaanaProvider: GaanaProvider, - val spotifyProvider: SpotifyProvider, + private val spotifyProvider: SpotifyProvider, val youtubeProvider: YoutubeProvider, val youtubeMusic: YoutubeMusic, val youtubeMp3: YoutubeMp3, private val dir: Dir ) { - private val db:DownloadRecordDatabaseQueries? + private val db: DownloadRecordDatabaseQueries? get() = dir.db?.downloadRecordDatabaseQueries - suspend fun query(link:String): PlatformQueryResult?{ - val result = when{ - //SPOTIFY - link.contains("spotify",true) -> + suspend fun query(link: String): PlatformQueryResult? { + val result = when { + // SPOTIFY + link.contains("spotify", true) -> spotifyProvider.query(link) - //YOUTUBE - link.contains("youtube.com",true) || link.contains("youtu.be",true) -> + // YOUTUBE + link.contains("youtube.com", true) || link.contains("youtu.be", true) -> youtubeProvider.query(link) - //GAANA - link.contains("gaana",true) -> + // GAANA + link.contains("gaana", true) -> gaanaProvider.query(link) else -> { @@ -56,7 +55,7 @@ class FetchPlatformQueryResult( } } result?.run { - withContext(Dispatchers.Default){ + withContext(Dispatchers.Default) { db?.add( folderType, title, link, coverUrl, trackList.size.toLong() ) @@ -64,4 +63,4 @@ class FetchPlatformQueryResult( } return result } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Picture.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Picture.kt index 39f5ef25..3e90c5d6 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Picture.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Picture.kt @@ -16,4 +16,4 @@ package com.shabinder.common.di -expect class Picture \ No newline at end of file +expect class Picture diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/TokenStore.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/TokenStore.kt index 4f31e1b9..0d139628 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/TokenStore.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/TokenStore.kt @@ -31,21 +31,21 @@ class TokenStore( private val db: TokenDBQueries? get() = dir.db?.tokenDBQueries - private fun save(token: TokenData){ - if(!token.access_token.isNullOrBlank() && token.expiry != null) + private fun save(token: TokenData) { + if (!token.access_token.isNullOrBlank() && token.expiry != null) db?.add(token.access_token!!, token.expiry!! + Clock.System.now().epochSeconds) } suspend fun getToken(): TokenData? { var token: TokenData? = db?.select()?.executeAsOneOrNull()?.let { - TokenData(it.accessToken,null,it.expiry) + TokenData(it.accessToken, null, it.expiry) } - logger.d{"System Time:${Clock.System.now().epochSeconds} , Token Expiry:${token?.expiry}"} - if(Clock.System.now().epochSeconds > token?.expiry ?:0 || token == null){ - logger.d{"Requesting New Token"} + logger.d { "System Time:${Clock.System.now().epochSeconds} , Token Expiry:${token?.expiry}" } + if (Clock.System.now().epochSeconds > token?.expiry ?: 0 || token == null) { + logger.d { "Requesting New Token" } token = authenticateSpotify() GlobalScope.launch { token?.access_token?.let { save(token) } } } return token } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt index 7393e5d3..909946ee 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt @@ -18,8 +18,7 @@ package com.shabinder.common.di import co.touchlab.kermit.Kermit import com.shabinder.common.models.PlatformQueryResult -import com.shabinder.database.Database -import io.ktor.client.* +import io.ktor.client.HttpClient expect class YoutubeProvider( httpClient: HttpClient, @@ -27,4 +26,4 @@ expect class YoutubeProvider( dir: Dir ) { suspend fun query(fullLink: String): PlatformQueryResult? -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt index a5901bf1..99a232af 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/gaana/GaanaRequests.kt @@ -19,11 +19,15 @@ package com.shabinder.common.di.gaana import com.shabinder.common.di.currentPlatform import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.corsProxy -import com.shabinder.common.models.gaana.* -import io.ktor.client.* -import io.ktor.client.request.* +import com.shabinder.common.models.gaana.GaanaAlbum +import com.shabinder.common.models.gaana.GaanaArtistDetails +import com.shabinder.common.models.gaana.GaanaArtistTracks +import com.shabinder.common.models.gaana.GaanaPlaylist +import com.shabinder.common.models.gaana.GaanaSong +import io.ktor.client.HttpClient +import io.ktor.client.request.get -val corsApi get() = if(currentPlatform is AllPlatforms.Js){ +val corsApi get() = if (currentPlatform is AllPlatforms.Js) { corsProxy.url } // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/" else "" @@ -33,7 +37,7 @@ private val BASE_URL get() = "${corsApi}https://api.gaana.com" interface GaanaRequests { - val httpClient:HttpClient + val httpClient: HttpClient /* * Api Request: http://api.gaana.com/?type=playlist&subtype=playlist_detail&seokey=gaana-dj-hindi-top-50-1&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON @@ -117,4 +121,4 @@ interface GaanaRequests { "$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit" ) } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt index dd91a539..9bf63da0 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt @@ -25,25 +25,25 @@ import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.gaana.GaanaTrack import com.shabinder.common.models.spotify.Source -import io.ktor.client.* +import io.ktor.client.HttpClient class GaanaProvider( override val httpClient: HttpClient, private val logger: Kermit, private val dir: Dir, -): GaanaRequests { +) : GaanaRequests { private val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg" - suspend fun query(fullLink: String): PlatformQueryResult?{ - //Link Schema: https://gaana.com/type/link + suspend fun query(fullLink: String): PlatformQueryResult? { + // Link Schema: https://gaana.com/type/link val gaanaLink = fullLink.substringAfter("gaana.com/") val link = gaanaLink.substringAfterLast('/', "error") val type = gaanaLink.substringBeforeLast('/', "error").substringAfterLast('/') - //Error - if (type == "Error" || link == "Error"){ + // Error + if (type == "Error" || link == "Error") { return null } return gaanaSearch( @@ -53,8 +53,8 @@ class GaanaProvider( } private suspend fun gaanaSearch( - type:String, - link:String, + type: String, + link: String, ): PlatformQueryResult { val result = PlatformQueryResult( folderType = "", @@ -64,22 +64,14 @@ class GaanaProvider( trackList = listOf(), Source.Gaana ) + logger.i { "GAANA SEARCH: $type - $link" } with(result) { when (type) { "song" -> { getGaanaSong(seokey = link).tracks.firstOrNull()?.also { folderType = "Tracks" subFolder = "" - if (dir.isPresent( - dir.finalOutputDir( - it.track_title, - folderType, - subFolder, - dir.defaultDir() - ) - )) {//Download Already Present!! - it.downloaded = DownloadStatus.Downloaded - } + it.updateStatusIfPresent(folderType, subFolder) trackList = listOf(it).toTrackDetailsList(folderType, subFolder) title = it.track_title coverUrl = it.artworkLink @@ -90,17 +82,7 @@ class GaanaProvider( folderType = "Albums" subFolder = link it.tracks.forEach { track -> - if (dir.isPresent( - dir.finalOutputDir( - track.track_title, - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - track.downloaded = DownloadStatus.Downloaded - } + track.updateStatusIfPresent(folderType, subFolder) } trackList = it.tracks.toTrackDetailsList(folderType, subFolder) title = link @@ -112,21 +94,11 @@ class GaanaProvider( folderType = "Playlists" subFolder = link it.tracks.forEach { track -> - if (dir.isPresent( - dir.finalOutputDir( - track.track_title, - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - track.downloaded = DownloadStatus.Downloaded - } + track.updateStatusIfPresent(folderType, subFolder) } trackList = it.tracks.toTrackDetailsList(folderType, subFolder) title = link - //coverUrl.value = "TODO" + // coverUrl.value = "TODO" coverUrl = gaanaPlaceholderImageUrl } } @@ -134,37 +106,27 @@ class GaanaProvider( folderType = "Artist" subFolder = link coverUrl = gaanaPlaceholderImageUrl - val artistDetails = - getGaanaArtistDetails(seokey = link).artist.firstOrNull() - ?.also { - title = it.name - coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl - } + getGaanaArtistDetails(seokey = link).artist.firstOrNull() + ?.also { + title = it.name + coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl + } getGaanaArtistTracks(seokey = link).also { it.tracks?.forEach { track -> - if (dir.isPresent( - dir.finalOutputDir( - track.track_title, - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - track.downloaded = DownloadStatus.Downloaded - } + track.updateStatusIfPresent(folderType, subFolder) } trackList = it.tracks?.toTrackDetailsList(folderType, subFolder) ?: emptyList() } } - else -> {//TODO Handle Error} + else -> { + // TODO Handle Error } } return result } } - private fun List.toTrackDetailsList(type:String, subFolder:String) = this.map { + private fun List.toTrackDetailsList(type: String, subFolder: String) = this.map { TrackDetails( title = it.track_title, artists = it.artist.map { artist -> artist?.name.toString() }, @@ -172,12 +134,25 @@ class GaanaProvider( albumArtPath = dir.imageCacheDir() + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg", albumName = it.album_title, year = it.release_date, - comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}", + comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}", trackUrl = it.lyrics_url, downloaded = it.downloaded ?: DownloadStatus.NotDownloaded, source = Source.Gaana, albumArtURL = it.artworkLink, - outputFilePath = dir.finalOutputDir(it.track_title,type, subFolder,dir.defaultDir()/*,".m4a"*/) + outputFilePath = dir.finalOutputDir(it.track_title, type, subFolder, dir.defaultDir()/*,".m4a"*/) ) } + private fun GaanaTrack.updateStatusIfPresent(folderType: String, subFolder: String) { + if (dir.isPresent( + dir.finalOutputDir( + track_title, + folderType, + subFolder, + dir.defaultDir() + ) + ) + ) { // Download Already Present!! + downloaded = DownloadStatus.Downloaded + } + } } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt index d49a2a32..475f8a6d 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt @@ -17,7 +17,11 @@ package com.shabinder.common.di.providers import co.touchlab.kermit.Kermit -import com.shabinder.common.di.* +import com.shabinder.common.di.Dir +import com.shabinder.common.di.TokenStore +import com.shabinder.common.di.currentPlatform +import com.shabinder.common.di.finalOutputDir +import com.shabinder.common.di.kotlinxSerializer import com.shabinder.common.di.spotify.SpotifyRequests import com.shabinder.common.di.spotify.authenticateSpotify import com.shabinder.common.models.AllPlatforms @@ -27,10 +31,10 @@ import com.shabinder.common.models.spotify.Album import com.shabinder.common.models.spotify.Image import com.shabinder.common.models.spotify.Source import com.shabinder.common.models.spotify.Track -import io.ktor.client.* -import io.ktor.client.features.* -import io.ktor.client.features.json.* -import io.ktor.client.request.* +import io.ktor.client.HttpClient +import io.ktor.client.features.defaultRequest +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.request.header import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -44,23 +48,22 @@ class SpotifyProvider( init { logger.d { "Creating Spotify Provider" } GlobalScope.launch(Dispatchers.Default) { - if(currentPlatform is AllPlatforms.Js){ + if (currentPlatform is AllPlatforms.Js) { authenticateSpotifyClient(override = true) - }else authenticateSpotifyClient() + } else authenticateSpotifyClient() } } - override suspend fun authenticateSpotifyClient(override:Boolean): HttpClient?{ - val token = if(override) authenticateSpotify() else tokenStore.getToken() - return if(token == null) { - logger.d{ "Please Check your Network Connection" } + override suspend fun authenticateSpotifyClient(override: Boolean): HttpClient? { + val token = if (override) authenticateSpotify() else tokenStore.getToken() + return if (token == null) { + logger.d { "Please Check your Network Connection" } null - } - else{ + } else { logger.d { "Spotify Provider Created with $token" } httpClient = HttpClient { defaultRequest { - header("Authorization","Bearer ${token.access_token}") + header("Authorization", "Bearer ${token.access_token}") } install(JsonFeature) { serializer = kotlinxSerializer @@ -72,9 +75,9 @@ class SpotifyProvider( override lateinit var httpClient: HttpClient - suspend fun query(fullLink: String): PlatformQueryResult?{ + suspend fun query(fullLink: String): PlatformQueryResult? { - if(!this::httpClient.isInitialized){ + if (!this::httpClient.isInitialized) { authenticateSpotifyClient() } @@ -82,20 +85,19 @@ class SpotifyProvider( "https://" + fullLink.substringAfterLast("https://").substringBefore(" ").trim() if (!spotifyLink.contains("open.spotify")) { - //Very Rare instance + // Very Rare instance spotifyLink = resolveLink(spotifyLink) } val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?') val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/') - if (type == "Error" || link == "Error") { return null } if (type == "episode" || type == "show") { - //TODO Implementation + // TODO Implementation return null } @@ -106,7 +108,7 @@ class SpotifyProvider( } private suspend fun spotifySearch( - type:String, + type: String, link: String ): PlatformQueryResult { val result = PlatformQueryResult( @@ -123,21 +125,13 @@ class SpotifyProvider( getTrack(link).also { folderType = "Tracks" subFolder = "" - if (dir.isPresent( - dir.finalOutputDir( - it.name.toString(), - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - it.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded - } + it.updateStatusIfPresent(folderType, subFolder) trackList = listOf(it).toTrackDetailsList(folderType, subFolder) title = it.name.toString() - coverUrl = (it.album?.images?.elementAtOrNull(1)?.url - ?: it.album?.images?.elementAtOrNull(0)?.url).toString() + coverUrl = ( + it.album?.images?.elementAtOrNull(1)?.url + ?: it.album?.images?.elementAtOrNull(0)?.url + ).toString() } } @@ -146,17 +140,7 @@ class SpotifyProvider( folderType = "Albums" subFolder = albumObject.name.toString() albumObject.tracks?.items?.forEach { - if (dir.isPresent( - dir.finalOutputDir( - it.name.toString(), - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - it.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded - } + it.updateStatusIfPresent(folderType, subFolder) it.album = Album( images = listOf( Image( @@ -168,12 +152,14 @@ class SpotifyProvider( } albumObject.tracks?.items?.toTrackDetailsList(folderType, subFolder).let { if (it.isNullOrEmpty()) { - //TODO Handle Error + // TODO Handle Error } else { trackList = it title = albumObject.name.toString() - coverUrl = (albumObject.images?.elementAtOrNull(1)?.url - ?: albumObject.images?.elementAtOrNull(0)?.url).toString() + coverUrl = ( + albumObject.images?.elementAtOrNull(1)?.url + ?: albumObject.images?.elementAtOrNull(0)?.url + ).toString() } } } @@ -183,27 +169,17 @@ class SpotifyProvider( folderType = "Playlists" subFolder = playlistObject.name.toString() val tempTrackList = mutableListOf() - //log("Tracks Fetched", playlistObject.tracks?.items?.size.toString()) + // log("Tracks Fetched", playlistObject.tracks?.items?.size.toString()) playlistObject.tracks?.items?.forEach { it.track?.let { it1 -> - if (dir.isPresent( - dir.finalOutputDir( - it1.name.toString(), - folderType, - subFolder, - dir.defaultDir() - ) - ) - ) {//Download Already Present!! - it1.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded - } + it1.updateStatusIfPresent(folderType, subFolder) tempTrackList.add(it1) } } var moreTracksAvailable = !playlistObject.tracks?.next.isNullOrBlank() while (moreTracksAvailable) { - //Check For More Tracks If available + // Check For More Tracks If available val moreTracks = getPlaylistTracks(link, offset = tempTrackList.size) moreTracks.items?.forEach { @@ -211,18 +187,18 @@ class SpotifyProvider( } moreTracksAvailable = !moreTracks.next.isNullOrBlank() } - //log("Total Tracks Fetched", tempTrackList.size.toString()) + // log("Total Tracks Fetched", tempTrackList.size.toString()) trackList = tempTrackList.toTrackDetailsList(folderType, subFolder) title = playlistObject.name.toString() coverUrl = playlistObject.images?.elementAtOrNull(1)?.url ?: playlistObject.images?.firstOrNull()?.url.toString() } - "episode" -> {//TODO + "episode" -> { // TODO } - "show" -> {//TODO + "show" -> { // TODO } else -> { - //TODO Handle Error + // TODO Handle Error } } } @@ -234,18 +210,18 @@ class SpotifyProvider( * Fetching Standard Link: https://open.spotify.com/playlist/37i9dQZF1DX9RwfGbeGQwP?si=iWz7B1tETiunDntnDo3lSQ&_branch_match_id=862039436205270630 * */ private suspend fun resolveLink( - url:String - ):String { + url: String + ): String { val response = getResponse(url) val regex = """https://open\.spotify\.com.+\w""".toRegex() return regex.find(response)?.value.toString() } - private fun List.toTrackDetailsList(type:String, subFolder:String) = this.map { + private fun List.toTrackDetailsList(type: String, subFolder: String) = this.map { TrackDetails( title = it.name.toString(), artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(), - durationSec = (it.duration_ms/1000).toInt(), + durationSec = (it.duration_ms / 1000).toInt(), albumArtPath = dir.imageCacheDir() + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg", albumName = it.album?.name, year = it.album?.release_date, @@ -254,7 +230,20 @@ class SpotifyProvider( downloaded = it.downloaded, source = Source.Spotify, albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString(), - outputFilePath = dir.finalOutputDir(it.name.toString(),type, subFolder,dir.defaultDir()/*,".m4a"*/) + outputFilePath = dir.finalOutputDir(it.name.toString(), type, subFolder, dir.defaultDir()/*,".m4a"*/) ) } -} \ No newline at end of file + private fun Track.updateStatusIfPresent(folderType: String, subFolder: String) { + if (dir.isPresent( + dir.finalOutputDir( + name.toString(), + folderType, + subFolder, + dir.defaultDir() + ) + ) + ) { // Download Already Present!! + downloaded = com.shabinder.common.models.DownloadStatus.Downloaded + } + } +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt index 29cfe40e..3bd580ae 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt @@ -21,19 +21,16 @@ import com.shabinder.common.di.Dir import com.shabinder.common.di.currentPlatform import com.shabinder.common.di.youtubeMp3.Yt1sMp3 import com.shabinder.common.models.AllPlatforms -import com.shabinder.common.models.CorsProxy -import com.shabinder.common.models.corsProxy -import com.shabinder.database.Database -import io.ktor.client.* +import io.ktor.client.HttpClient class YoutubeMp3( override val httpClient: HttpClient, private val logger: Kermit, private val dir: Dir, -):Yt1sMp3 { - suspend fun getMp3DownloadLink(videoID:String):String? = getLinkFromYt1sMp3(videoID)?.let{ +) : Yt1sMp3 { + suspend fun getMp3DownloadLink(videoID: String): String? = getLinkFromYt1sMp3(videoID)?.let { if (currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/) "https://kind-grasshopper-73.telebit.io/cors/$it" else it } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt index 0fc9780b..a1acf12d 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt @@ -21,21 +21,32 @@ import com.shabinder.common.di.gaana.corsApi import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.YoutubeTrack import com.willowtreeapps.fuzzywuzzy.diffutils.FuzzySearch -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.http.* -import kotlinx.serialization.json.* +import io.ktor.client.HttpClient +import io.ktor.client.request.headers +import io.ktor.client.request.post +import io.ktor.http.ContentType +import io.ktor.http.contentType +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import kotlinx.serialization.json.put +import kotlinx.serialization.json.putJsonObject import kotlin.math.absoluteValue private const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30" class YoutubeMusic constructor( private val logger: Kermit, - private val httpClient:HttpClient, + private val httpClient: HttpClient, ) { private val tag = "YT Music" - suspend fun getYTIDBestMatch(query: String,trackDetails: TrackDetails):String?{ + suspend fun getYTIDBestMatch(query: String, trackDetails: TrackDetails): String? { return sortByBestMatch( getYTTracks(query), trackName = trackDetails.title, @@ -43,7 +54,7 @@ class YoutubeMusic constructor( trackDurationSec = trackDetails.durationSec ).keys.firstOrNull() } - private suspend fun getYTTracks(query: String):List{ + private suspend fun getYTTracks(query: String): List { val youtubeTracks = mutableListOf() val responseObj = Json.parseToJsonElement(getYoutubeMusicResponse(query)) @@ -54,33 +65,35 @@ class YoutubeMusic constructor( val resultBlocks = mutableListOf() if (contentBlocks != null) { - for (cBlock in contentBlocks){ + for (cBlock in contentBlocks) { /** *Ignore user-suggestion *The 'itemSectionRenderer' field is for user notices (stuff like - 'showing *results for xyz, search for abc instead') we have no use for them, the for *loop below if throw a keyError if we don't ignore them */ - if(cBlock.jsonObject.containsKey("itemSectionRenderer")){ + if (cBlock.jsonObject.containsKey("itemSectionRenderer")) { continue } - for(contents in cBlock.jsonObject["musicShelfRenderer"]?.jsonObject?.get("contents")?.jsonArray - ?: listOf()){ + for ( + contents in cBlock.jsonObject["musicShelfRenderer"]?.jsonObject?.get("contents")?.jsonArray + ?: listOf() + ) { /** * apparently content Blocks without an 'overlay' field don't have linkBlocks * I have no clue what they are and why there even exist * - if(!contents.containsKey("overlay")){ - println(contents) - continue - TODO check and correct - }*/ + if(!contents.containsKey("overlay")){ + println(contents) + continue + TODO check and correct + }*/ val result = contents.jsonObject["musicResponsiveListItemRenderer"] ?.jsonObject?.get("flexColumns")?.jsonArray - //Add the linkBlock + // Add the linkBlock val linkBlock = contents.jsonObject["musicResponsiveListItemRenderer"] ?.jsonObject?.get("overlay") ?.jsonObject?.get("musicItemThumbnailOverlayRenderer") @@ -122,7 +135,7 @@ class YoutubeMusic constructor( ! we do so only if their Type is 'Song' or 'Video */ - for(result in resultBlocks){ + for (result in resultBlocks) { // Blindly gather available details val availableDetails = mutableListOf() @@ -137,33 +150,33 @@ class YoutubeMusic constructor( ! other constituents of a result block will lead to errors, hence the 'in ! result[:-1] ,i.e., skip last element in array ' */ - for(detailArray in result.subList(0,result.size-1)){ - for(detail in detailArray.jsonArray){ - if(detail.jsonObject["musicResponsiveListItemFlexColumnRenderer"]?.jsonObject?.size?:0 < 2) continue + for (detailArray in result.subList(0, result.size - 1)) { + for (detail in detailArray.jsonArray) { + if (detail.jsonObject["musicResponsiveListItemFlexColumnRenderer"]?.jsonObject?.size ?: 0 < 2) continue // if not a dummy, collect All Variables val details = detail.jsonObject["musicResponsiveListItemFlexColumnRenderer"] ?.jsonObject?.get("text") ?.jsonObject?.get("runs")?.jsonArray ?: listOf() - for (d in details){ + for (d in details) { d.jsonObject["text"]?.jsonPrimitive?.contentOrNull?.let { - if(it != " • "){ + if (it != " • ") { availableDetails.add(it) } } } } } - //logger.d("YT Music details"){availableDetails.toString()} + // logger.d("YT Music details"){availableDetails.toString()} /* ! Filter Out non-Song/Video results and incomplete results here itself ! From what we know about detail order, note that [1] - indicate result type */ - if ( availableDetails.size == 5 && availableDetails[1] in listOf("Song","Video") ){ + if (availableDetails.size == 5 && availableDetails[1] in listOf("Song", "Video")) { // skip if result is in hours instead of minutes (no song is that long) - if(availableDetails[4].split(':').size != 2) continue + if (availableDetails[4].split(':').size != 2) continue /* ! grab Video ID @@ -173,7 +186,7 @@ class YoutubeMusic constructor( ! reference the dict keys by index */ - val videoId:String? = result.last().jsonObject["watchEndpoint"]?.jsonObject?.get("videoId")?.jsonPrimitive?.content + val videoId: String? = result.last().jsonObject["watchEndpoint"]?.jsonObject?.get("videoId")?.jsonPrimitive?.content val ytTrack = YoutubeTrack( name = availableDetails[0], type = availableDetails[1], @@ -185,64 +198,63 @@ class YoutubeMusic constructor( } } } - //logger.d {youtubeTracks.joinToString("\n")} + // logger.d {youtubeTracks.joinToString("\n")} return youtubeTracks } private fun sortByBestMatch( - ytTracks:List, - trackName:String, - trackArtists:List, - trackDurationSec:Int, - ):Map{ + ytTracks: List, + trackName: String, + trackArtists: List, + trackDurationSec: Int, + ): Map { /* * "linksWithMatchValue" is map with Youtube VideoID and its rating/match with 100 as Max Value **/ - val linksWithMatchValue = mutableMapOf() + val linksWithMatchValue = mutableMapOf() - for (result in ytTracks){ + for (result in ytTracks) { // LoweCasing Name to match Properly // most song results on youtube go by $artist - $songName or artist1/artist2 var hasCommonWord = false - val resultName = result.name?.toLowerCase()?.replace("-"," ")?.replace("/"," ") ?: "" + val resultName = result.name?.toLowerCase()?.replace("-", " ")?.replace("/", " ") ?: "" val trackNameWords = trackName.toLowerCase().split(" ") - for (nameWord in trackNameWords){ - if (nameWord.isNotBlank() && FuzzySearch.partialRatio(nameWord,resultName) > 85) hasCommonWord = true + for (nameWord in trackNameWords) { + if (nameWord.isNotBlank() && FuzzySearch.partialRatio(nameWord, resultName) > 85) hasCommonWord = true } // Skip this Result if No Word is Common in Name if (!hasCommonWord) { - //log("YT Api Removing", result.toString()) + // log("YT Api Removing", result.toString()) continue } - // Find artist match // Will Be Using Fuzzy Search Because YT Spelling might be mucked up // match = (no of artist names in result) / (no. of artist names on spotify) * 100 var artistMatchNumber = 0 - if(result.type == "Song"){ - for (artist in trackArtists){ - if(FuzzySearch.ratio(artist.toLowerCase(),result.artist?.toLowerCase() ?: "") > 85) + if (result.type == "Song") { + for (artist in trackArtists) { + if (FuzzySearch.ratio(artist.toLowerCase(), result.artist?.toLowerCase() ?: "") > 85) artistMatchNumber++ } - }else{//i.e. is a Video + } else { // i.e. is a Video for (artist in trackArtists) { - if(FuzzySearch.partialRatio(artist.toLowerCase(),result.name?.toLowerCase() ?: "") > 85) + if (FuzzySearch.partialRatio(artist.toLowerCase(), result.name?.toLowerCase() ?: "") > 85) artistMatchNumber++ } } - if(artistMatchNumber == 0) { - //logger.d{ "YT Api Removing: $result" } + if (artistMatchNumber == 0) { + // logger.d{ "YT Api Removing: $result" } continue } - val artistMatch = (artistMatchNumber / trackArtists.size ) * 100 + val artistMatch = (artistMatchNumber / trackArtists.size) * 100 // Duration Match /*! time match = 100 - (delta(duration)**2 / original duration * 100) @@ -250,33 +262,35 @@ class YoutubeMusic constructor( ! seconds, we need to amplify the delta if it is to have any meaningful impact ! wen we calculate the avg match value*/ val difference = result.duration?.split(":")?.get(0)?.toInt()?.times(60) - ?.plus(result.duration?.split(":")?.get(1)?.toInt()?:0) + ?.plus(result.duration?.split(":")?.get(1)?.toInt() ?: 0) ?.minus(trackDurationSec)?.absoluteValue ?: 0 - val nonMatchValue :Float= ((difference*difference).toFloat()/trackDurationSec.toFloat()) - val durationMatch = 100 - (nonMatchValue*100) + val nonMatchValue: Float = ((difference * difference).toFloat() / trackDurationSec.toFloat()) + val durationMatch = 100 - (nonMatchValue * 100) - val avgMatch = (artistMatch + durationMatch)/2 + val avgMatch = (artistMatch + durationMatch) / 2 linksWithMatchValue[result.videoId.toString()] = avgMatch.toInt() } - //logger.d("YT Api Result"){"$trackName - $linksWithMatchValue"} - return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap() + // logger.d("YT Api Result"){"$trackName - $linksWithMatchValue"} + return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also { + logger.d(tag) { "Match Found for $trackName - ${!it.isNullOrEmpty()}" } + } } - private suspend fun getYoutubeMusicResponse(query: String):String{ - return httpClient.post("${corsApi}https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey"){ + private suspend fun getYoutubeMusicResponse(query: String): String { + return httpClient.post("${corsApi}https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey") { contentType(ContentType.Application.Json) - headers{ - append("referer","https://music.youtube.com/search") + headers { + append("referer", "https://music.youtube.com/search") } body = buildJsonObject { - putJsonObject("context"){ - putJsonObject("client"){ - put("clientName" ,"WEB_REMIX") - put("clientVersion" ,"0.1") + putJsonObject("context") { + putJsonObject("client") { + put("clientName", "WEB_REMIX") + put("clientVersion", "0.1") } } - put("query",query) + put("query", query) } } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyAuth.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyAuth.kt index 5f65b2c4..956a0772 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyAuth.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyAuth.kt @@ -19,17 +19,17 @@ package com.shabinder.common.di.spotify import com.shabinder.common.di.isInternetAvailable import com.shabinder.common.di.kotlinxSerializer import com.shabinder.common.models.spotify.TokenData -import io.ktor.client.* -import io.ktor.client.features.auth.* -import io.ktor.client.features.auth.providers.* -import io.ktor.client.features.json.* -import io.ktor.client.request.* -import io.ktor.client.request.forms.* -import io.ktor.http.* +import io.ktor.client.HttpClient +import io.ktor.client.features.auth.Auth +import io.ktor.client.features.auth.providers.basic +import io.ktor.client.features.json.JsonFeature +import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.post +import io.ktor.http.Parameters suspend fun authenticateSpotify(): TokenData? { - return if(isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token"){ - body = FormDataContent(Parameters.build { append("grant_type","client_credentials") }) + return if (isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token") { + body = FormDataContent(Parameters.build { append("grant_type", "client_credentials") }) } else null } @@ -49,4 +49,4 @@ private val spotifyAuthClient by lazy { serializer = kotlinxSerializer } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt index ac8a49ca..16d947f3 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/spotify/SpotifyRequests.kt @@ -21,16 +21,16 @@ import com.shabinder.common.models.spotify.Album import com.shabinder.common.models.spotify.PagingObjectPlaylistTrack import com.shabinder.common.models.spotify.Playlist import com.shabinder.common.models.spotify.Track -import io.ktor.client.* -import io.ktor.client.request.* +import io.ktor.client.HttpClient +import io.ktor.client.request.get private val BASE_URL get() = "${corsApi}https://api.spotify.com/v1" interface SpotifyRequests { - val httpClient:HttpClient + val httpClient: HttpClient - suspend fun authenticateSpotifyClient(override:Boolean = false):HttpClient? + suspend fun authenticateSpotifyClient(override: Boolean = false): HttpClient? suspend fun getPlaylist(playlistID: String): Playlist { return httpClient.get("$BASE_URL/playlists/$playlistID") @@ -48,7 +48,7 @@ interface SpotifyRequests { return httpClient.get("$BASE_URL/tracks/$id") } - suspend fun getEpisode(id: String?) : Track { + suspend fun getEpisode(id: String?): Track { return httpClient.get("$BASE_URL/episodes/$id") } @@ -60,7 +60,7 @@ interface SpotifyRequests { return httpClient.get("$BASE_URL/albums/$id") } - suspend fun getResponse(url:String):String{ + suspend fun getResponse(url: String): String { return httpClient.get(url) } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/ParallelExecutor.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/ParallelExecutor.kt index 25cc87ce..48687115 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/ParallelExecutor.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/ParallelExecutor.kt @@ -21,12 +21,19 @@ package com.shabinder.common.di.utils // implementation("org.jetbrains.kotlinx:atomicfu:0.14.4") // Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e -import io.ktor.utils.io.core.* +import io.ktor.utils.io.core.Closeable import kotlinx.atomicfu.atomic -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import kotlinx.coroutines.selects.* -import kotlin.coroutines.* +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.selects.select +import kotlinx.coroutines.withContext +import kotlin.coroutines.CoroutineContext class ParallelExecutor( parentContext: CoroutineContext, @@ -38,12 +45,10 @@ class ParallelExecutor( private val killQueue = Channel(Channel.UNLIMITED) private val operationQueue = Channel>(Channel.RENDEZVOUS) - init { startOrStopProcessors(expectedCount = concurrentOperationLimit.value, actualCount = 0) } - override fun close() { if (!isClosed.compareAndSet(expect = false, update = true)) return @@ -55,7 +60,6 @@ class ParallelExecutor( coroutineContext.cancel(cause) } - private fun CoroutineScope.launchProcessor() = launch { while (true) { val operation = select?> { @@ -67,7 +71,6 @@ class ParallelExecutor( } } - suspend fun execute(block: suspend () -> Result): Result = withContext(coroutineContext) { val operation = Operation(block) @@ -76,7 +79,6 @@ class ParallelExecutor( operation.result.await() } - // TODO This launches all coroutines in advance even if they're never needed. Find a lazy way to do this. fun setConcurrentOperationLimit(limit: Int) { require(limit >= 1) { "'limit' must be greater than zero: $limit" } @@ -85,7 +87,6 @@ class ParallelExecutor( startOrStopProcessors(expectedCount = limit, actualCount = concurrentOperationLimit.getAndSet(limit)) } - private fun startOrStopProcessors(expectedCount: Int, actualCount: Int) { if (expectedCount == actualCount) return @@ -105,7 +106,6 @@ class ParallelExecutor( repeat(-change) { killQueue.offer(Unit) } } - private class Operation( private val block: suspend () -> Result, ) { @@ -114,12 +114,10 @@ class ParallelExecutor( val result: Deferred get() = _result - suspend fun execute() { try { _result.complete(block()) - } - catch (e: Throwable) { + } catch (e: Throwable) { _result.completeExceptionally(e) } } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt index 3751822f..8c096aca 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/utils/Utils.kt @@ -16,8 +16,6 @@ package com.shabinder.common.di.utils - - /** * Removing Illegal Chars from File Name * **/ @@ -57,4 +55,4 @@ fun removeIllegalChars(fileName: String): String { name = name.replace(":".toRegex(), "") name = name.replace("\\|".toRegex(), "") return name -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt index 06a4d4a9..898aba06 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/youtubeMp3/Yt1sMp3.kt @@ -17,10 +17,10 @@ package com.shabinder.common.di.youtubeMp3 import com.shabinder.common.di.gaana.corsApi -import io.ktor.client.* -import io.ktor.client.request.* -import io.ktor.client.request.forms.* -import io.ktor.http.* +import io.ktor.client.HttpClient +import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.post +import io.ktor.http.Parameters import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive @@ -35,29 +35,33 @@ interface Yt1sMp3 { /* * Downloadable Mp3 Link for YT videoID. * */ - suspend fun getLinkFromYt1sMp3(videoID: String):String? = - getConvertedMp3Link(videoID,getKey(videoID))?.get("dlink")?.jsonPrimitive?.toString()?.replace("\"", "") + suspend fun getLinkFromYt1sMp3(videoID: String): String? = + getConvertedMp3Link(videoID, getKey(videoID))?.get("dlink")?.jsonPrimitive?.toString()?.replace("\"", "") /* * POST:https://yt1s.com/api/ajaxSearch/index * Body Form= q:yt video link ,vt:format=mp3 * */ - private suspend fun getKey(videoID:String):String{ - val response:JsonObject? = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index"){ - body = FormDataContent(Parameters.build { - append("q","https://www.youtube.com/watch?v=$videoID") - append("vt","mp3") - }) + private suspend fun getKey(videoID: String): String { + val response: JsonObject? = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index") { + body = FormDataContent( + Parameters.build { + append("q", "https://www.youtube.com/watch?v=$videoID") + append("vt", "mp3") + } + ) } return response?.get("kc")?.jsonPrimitive.toString() } - private suspend fun getConvertedMp3Link(videoID: String,key:String):JsonObject?{ - return httpClient.post("${corsApi}https://yt1s.com/api/ajaxConvert/convert"){ - body = FormDataContent(Parameters.build { - append("vid", videoID) - append("k",key) - }) + private suspend fun getConvertedMp3Link(videoID: String, key: String): JsonObject? { + return httpClient.post("${corsApi}https://yt1s.com/api/ajaxConvert/convert") { + body = FormDataContent( + Parameters.build { + append("vid", videoID) + append("k", key) + } + ) } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt index 00650a69..0df08c0b 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopActual.kt @@ -25,7 +25,7 @@ import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails -import io.ktor.client.request.* +import io.ktor.client.request.head import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableSharedFlow @@ -33,22 +33,22 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -actual fun openPlatform(packageID:String, platformLink:String){ - //TODO +actual fun openPlatform(packageID: String, platformLink: String) { + // TODO } actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm actual val dispatcherIO = Dispatchers.IO -actual fun shareApp(){ - //TODO +actual fun shareApp() { + // TODO } -actual fun giveDonation(){ - //TODO +actual fun giveDonation() { + // TODO } -actual fun queryActiveTracks(){} +actual fun queryActiveTracks() {} /* * Refactor This @@ -65,36 +65,39 @@ private suspend fun isInternetAvailable(): Boolean { } } -actual val isInternetAvailable:Boolean - get(){ +actual val isInternetAvailable: Boolean + get() { var result = false val job = GlobalScope.launch { result = isInternetAvailable() } - while(job.isActive){} + while (job.isActive) {} return result } -val DownloadProgressFlow: MutableSharedFlow> = MutableSharedFlow(1) +val DownloadProgressFlow: MutableSharedFlow> = MutableSharedFlow(1) -//Scope Allowing 4 Parallel Downloads +// Scope Allowing 4 Parallel Downloads val DownloadScope = ParallelExecutor(Dispatchers.IO) actual suspend fun downloadTracks( list: List, fetcher: FetchPlatformQueryResult, dir: Dir -){ +) { list.forEach { DownloadScope.execute { // Send Download to Pool. - if (!it.videoID.isNullOrBlank()) {//Video ID already known! - downloadTrack(it.videoID!!, it,dir::saveFileWithMetadata) + if (!it.videoID.isNullOrBlank()) { // Video ID already known! + downloadTrack(it.videoID!!, it, dir::saveFileWithMetadata) } else { val searchQuery = "${it.title} - ${it.artists.joinToString(",")}" - val videoId = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery,it) + val videoId = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery, it) if (videoId.isNullOrBlank()) { - DownloadProgressFlow.emit(DownloadProgressFlow.replayCache.getOrElse(0 - ) { hashMapOf() }.apply { set(it.title,DownloadStatus.Failed) }) - } else {//Found Youtube Video ID - downloadTrack(videoId, it,dir::saveFileWithMetadata) + DownloadProgressFlow.emit( + DownloadProgressFlow.replayCache.getOrElse( + 0 + ) { hashMapOf() }.apply { set(it.title, DownloadStatus.Failed) } + ) + } else { // Found Youtube Video ID + downloadTrack(videoId, it, dir::saveFileWithMetadata) } } } @@ -106,7 +109,7 @@ private val ytDownloader = YoutubeDownloader() suspend fun downloadTrack( videoID: String, trackDetails: TrackDetails, - saveFileWithMetaData:suspend (mp3ByteArray:ByteArray, trackDetails: TrackDetails) -> Unit + saveFileWithMetaData: suspend (mp3ByteArray: ByteArray, trackDetails: TrackDetails) -> Unit ) { try { val audioData = ytDownloader.getVideo(videoID).getData() @@ -114,28 +117,37 @@ suspend fun downloadTrack( audioData?.let { format -> val url: String = format.url() downloadFile(url).collect { - when(it){ + when (it) { is DownloadResult.Error -> { - DownloadProgressFlow.emit(DownloadProgressFlow.replayCache.getOrElse(0 - ) { hashMapOf() }.apply { set(trackDetails.title,DownloadStatus.Failed) }) + DownloadProgressFlow.emit( + DownloadProgressFlow.replayCache.getOrElse( + 0 + ) { hashMapOf() }.apply { set(trackDetails.title, DownloadStatus.Failed) } + ) } is DownloadResult.Progress -> { - DownloadProgressFlow.emit(DownloadProgressFlow.replayCache.getOrElse(0 - ) { hashMapOf() }.apply { set(trackDetails.title,DownloadStatus.Downloading(it.progress)) }) + DownloadProgressFlow.emit( + DownloadProgressFlow.replayCache.getOrElse( + 0 + ) { hashMapOf() }.apply { set(trackDetails.title, DownloadStatus.Downloading(it.progress)) } + ) } - is DownloadResult.Success -> {//Todo clear map - saveFileWithMetaData(it.byteArray,trackDetails) - DownloadProgressFlow.emit(DownloadProgressFlow.replayCache.getOrElse(0 - ) { hashMapOf() }.apply { set(trackDetails.title,DownloadStatus.Downloaded) }) + is DownloadResult.Success -> { // Todo clear map + saveFileWithMetaData(it.byteArray, trackDetails) + DownloadProgressFlow.emit( + DownloadProgressFlow.replayCache.getOrElse( + 0 + ) { hashMapOf() }.apply { set(trackDetails.title, DownloadStatus.Downloaded) } + ) } } } } - }catch (e: java.lang.Exception){ + } catch (e: java.lang.Exception) { e.printStackTrace() } } -fun YoutubeVideo.getData(): Format?{ +fun YoutubeVideo.getData(): Format? { return try { findAudioWithQuality(AudioQuality.medium)?.get(0) as Format } catch (e: java.lang.IndexOutOfBoundsException) { @@ -149,4 +161,4 @@ fun YoutubeVideo.getData(): Format?{ } } } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopDir.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopDir.kt index 3a707082..dbcbe0ab 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopDir.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopDir.kt @@ -25,6 +25,7 @@ import com.shabinder.database.Database import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.jetbrains.skija.Image import java.awt.image.BufferedImage import java.io.ByteArrayOutputStream @@ -35,13 +36,10 @@ import java.net.HttpURLConnection import java.net.URL import javax.imageio.ImageIO - - - actual class Dir actual constructor( private val logger: Kermit, private val database: Database?, - ) { +) { init { createDirectories() @@ -50,38 +48,33 @@ actual class Dir actual constructor( actual fun fileSeparator(): String = File.separator actual fun imageCacheDir(): String = System.getProperty("user.home") + - fileSeparator() + "SpotiFlyer/.images" + fileSeparator() + fileSeparator() + "SpotiFlyer/.images" + fileSeparator() actual fun defaultDir(): String = System.getProperty("user.home") + fileSeparator() + - "SpotiFlyer" + fileSeparator() + "SpotiFlyer" + fileSeparator() actual fun isPresent(path: String): Boolean = File(path).exists() - actual fun createDirectory(dirPath:String){ + actual fun createDirectory(dirPath: String) { val yourAppDir = File(dirPath) - if(!yourAppDir.exists() && !yourAppDir.isDirectory) - { // create empty directory - if (yourAppDir.mkdirs()) - {logger.i{"$dirPath created"}} - else - { - logger.e{"Unable to create Dir: $dirPath!"} + if (!yourAppDir.exists() && !yourAppDir.isDirectory) { // create empty directory + if (yourAppDir.mkdirs()) { logger.i { "$dirPath created" } } else { + logger.e { "Unable to create Dir: $dirPath!" } } - } - else { + } else { logger.i { "$dirPath already exists" } } } actual suspend fun clearCache() { - File(imageCacheDir()). deleteRecursively() + File(imageCacheDir()).deleteRecursively() } - actual suspend fun cacheImage(image: Any,path:String) { + actual suspend fun cacheImage(image: Any, path: String) { try { (image as? BufferedImage)?.let { - ImageIO.write(it,"jpeg", File(path)) + ImageIO.write(it, "jpeg", File(path)) } } catch (e: IOException) { e.printStackTrace() @@ -89,9 +82,9 @@ actual class Dir actual constructor( } @Suppress("BlockingMethodInNonBlockingContext") - actual suspend fun saveFileWithMetadata( - mp3ByteArray: ByteArray, - trackDetails: TrackDetails + actual suspend fun saveFileWithMetadata( + mp3ByteArray: ByteArray, + trackDetails: TrackDetails ) { val file = File(trackDetails.outputFilePath) file.writeBytes(mp3ByteArray) @@ -101,7 +94,7 @@ actual class Dir actual constructor( .setId3v1Tags(trackDetails) .setId3v2TagsAndSaveFile(trackDetails) } - actual fun addToLibrary(path:String){} + actual fun addToLibrary(path: String) {} actual suspend fun loadImage(url: String): Picture { val cachePath = imageCacheDir() + getNameURL(url) @@ -114,30 +107,33 @@ actual class Dir actual constructor( return try { ImageIO.read(File(cachePath))?.toImageBitmap() } catch (e: Exception) { - //e.printStackTrace() + // e.printStackTrace() null } } - private suspend fun freshImage(url:String): ImageBitmap?{ - return try { - val source = URL(url) - val connection: HttpURLConnection = source.openConnection() as HttpURLConnection - connection.connectTimeout = 5000 - connection.connect() + @Suppress("BlockingMethodInNonBlockingContext") + private suspend fun freshImage(url: String): ImageBitmap? { + return withContext(Dispatchers.IO) { + try { + val source = URL(url) + val connection: HttpURLConnection = source.openConnection() as HttpURLConnection + connection.connectTimeout = 5000 + connection.connect() - val input: InputStream = connection.inputStream - val result: BufferedImage? = ImageIO.read(input) + val input: InputStream = connection.inputStream + val result: BufferedImage? = ImageIO.read(input) - if (result != null) { - GlobalScope.launch(Dispatchers.IO) { //TODO Refactor - cacheImage(result,imageCacheDir() + getNameURL(url)) - } - result.toImageBitmap() - } else null - } catch (e: Exception) { - e.printStackTrace() - null + if (result != null) { + GlobalScope.launch(Dispatchers.IO) { // TODO Refactor + cacheImage(result, imageCacheDir() + getNameURL(url)) + } + result.toImageBitmap() + } else null + } catch (e: Exception) { + e.printStackTrace() + null + } } } @@ -148,7 +144,7 @@ fun BufferedImage.toImageBitmap() = Image.makeFromEncoded( toByteArray(this) ).asImageBitmap() -private fun toByteArray(bitmap: BufferedImage) : ByteArray { +private fun toByteArray(bitmap: BufferedImage): ByteArray { val baOs = ByteArrayOutputStream() ImageIO.write(bitmap, "png", baOs) return baOs.toByteArray() diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopPicture.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopPicture.kt index ab7be31d..35b485a7 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopPicture.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/DesktopPicture.kt @@ -20,4 +20,4 @@ import androidx.compose.ui.graphics.ImageBitmap actual data class Picture( var image: ImageBitmap? -) \ No newline at end of file +) diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/ID3Tagging.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/ID3Tagging.kt index 662abe4e..73ad2a67 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/ID3Tagging.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/ID3Tagging.kt @@ -32,7 +32,6 @@ fun Mp3File.removeAllTags(): Mp3File { return this } - /** * Modifying Mp3 with MetaData! **/ @@ -49,7 +48,7 @@ fun Mp3File.setId3v1Tags(track: TrackDetails): Mp3File { } @Suppress("BlockingMethodInNonBlockingContext") -suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails){ +suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails) { val id3v2Tag = ID3v24Tag().apply { artist = track.artists.joinToString(",") title = track.title @@ -59,37 +58,37 @@ suspend fun Mp3File.setId3v2TagsAndSaveFile(track: TrackDetails){ lyrics = "Gonna Implement Soon" url = track.trackUrl } - try{ + try { val art = File(track.albumArtPath) val bytesArray = ByteArray(art.length().toInt()) val fis = FileInputStream(art) - fis.read(bytesArray) //read file into bytes[] + fis.read(bytesArray) // read file into bytes[] fis.close() id3v2Tag.setAlbumImage(bytesArray, "image/jpeg") this.id3v2Tag = id3v2Tag saveFile(track.outputFilePath) - }catch (e: java.io.FileNotFoundException){ + } catch (e: java.io.FileNotFoundException) { try { - //Image Still Not Downloaded! - //Lets Download Now and Write it into Album Art + // Image Still Not Downloaded! + // Lets Download Now and Write it into Album Art downloadFile(track.albumArtURL).collect { - when(it){ - is DownloadResult.Error -> {}//Error + when (it) { + is DownloadResult.Error -> {} // Error is DownloadResult.Success -> { id3v2Tag.setAlbumImage(it.byteArray, "image/jpeg") this.id3v2Tag = id3v2Tag saveFile(track.outputFilePath) } - is DownloadResult.Progress -> {}//Nothing for Now , no progress bar to show + is DownloadResult.Progress -> {} // Nothing for Now , no progress bar to show } } - }catch (e: Exception){ - //log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}") + } catch (e: Exception) { + // log("Error", "Couldn't Write Mp3 Album Art, error: ${e.stackTrace}") } } } -fun Mp3File.saveFile(filePath: String){ +fun Mp3File.saveFile(filePath: String) { save(filePath.substringBeforeLast('.') + ".new.mp3") val m4aFile = File(filePath) m4aFile.delete() diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt index 015ec158..77038f5d 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt @@ -23,13 +23,13 @@ import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.spotify.Source -import io.ktor.client.* +import io.ktor.client.HttpClient actual class YoutubeProvider actual constructor( private val httpClient: HttpClient, private val logger: Kermit, private val dir: Dir, -){ +) { private val ytDownloader: YoutubeDownloader = YoutubeDownloader() /* @@ -41,34 +41,34 @@ actual class YoutubeProvider actual constructor( private val sampleDomain2 = "youtube.com" private val sampleDomain3 = "youtu.be" - actual suspend fun query(fullLink: String): PlatformQueryResult?{ + actual suspend fun query(fullLink: String): PlatformQueryResult? { val link = fullLink.removePrefix("https://").removePrefix("http://") - if(link.contains("playlist",true) || link.contains("list",true)){ + if (link.contains("playlist", true) || link.contains("list", true)) { // Given Link is of a Playlist - logger.i{ link } + logger.i { link } val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&").substringBefore("?") return getYTPlaylist( playlistId ) - }else{//Given Link is of a Video + } else { // Given Link is of a Video var searchId = "error" - when{ - link.contains(sampleDomain1,true) -> {//Youtube Music - searchId = link.substringAfterLast("/","error").substringBefore("&").substringAfterLast("=") + when { + link.contains(sampleDomain1, true) -> { // Youtube Music + searchId = link.substringAfterLast("/", "error").substringBefore("&").substringAfterLast("=") } - link.contains(sampleDomain2,true) -> {//Standard Youtube Link - searchId = link.substringAfterLast("=","error").substringBefore("&") + link.contains(sampleDomain2, true) -> { // Standard Youtube Link + searchId = link.substringAfterLast("=", "error").substringBefore("&") } - link.contains(sampleDomain3,true) -> {//Shortened Youtube Link - searchId = link.substringAfterLast("/","error").substringBefore("&") + link.contains(sampleDomain3, true) -> { // Shortened Youtube Link + searchId = link.substringAfterLast("/", "error").substringBefore("&") } } - return if(searchId != "error") { + return if (searchId != "error") { getYTTrack( searchId ) - }else{ - logger.d{"Your Youtube Link is not of a Video!!"} + } else { + logger.d { "Your Youtube Link is not of a Video!!" } null } } @@ -76,7 +76,7 @@ actual class YoutubeProvider actual constructor( private suspend fun getYTPlaylist( searchId: String - ): PlatformQueryResult?{ + ): PlatformQueryResult? { val result = PlatformQueryResult( folderType = "", subFolder = "", @@ -94,7 +94,7 @@ actual class YoutubeProvider actual constructor( val videos = playlist.videos() coverUrl = "https://i.ytimg.com/vi/${ - videos.firstOrNull()?.videoId() + videos.firstOrNull()?.videoId() }/hqdefault.jpg" title = name @@ -108,11 +108,11 @@ actual class YoutubeProvider actual constructor( albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg", downloaded = if (dir.isPresent( dir.finalOutputDir( - itemName = it.title(), - type = folderType, - subFolder = subFolder, - dir.defaultDir() - ) + itemName = it.title(), + type = folderType, + subFolder = subFolder, + dir.defaultDir() + ) ) ) DownloadStatus.Downloaded @@ -125,16 +125,16 @@ actual class YoutubeProvider actual constructor( } } catch (e: Exception) { e.printStackTrace() - logger.d{"An Error Occurred While Processing!"} + logger.d { "An Error Occurred While Processing!" } } } - return if(result.title.isNotBlank()) result + return if (result.title.isNotBlank()) result else null } @Suppress("DefaultLocale") private suspend fun getYTTrack( - searchId:String, + searchId: String, ): PlatformQueryResult? { val result = PlatformQueryResult( folderType = "", @@ -143,15 +143,15 @@ actual class YoutubeProvider actual constructor( coverUrl = "", trackList = listOf(), Source.YouTube - ).apply{ + ).apply { try { - logger.i{searchId} + logger.i { searchId } val video = ytDownloader.getVideo(searchId) coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg" val detail = video?.details() val name = detail?.title()?.replace(detail.author()!!.toUpperCase(), "", true) ?: detail?.title() ?: "" - //logger.i{ detail.toString() } + // logger.i{ detail.toString() } trackList = listOf( TrackDetails( title = name, @@ -162,11 +162,11 @@ actual class YoutubeProvider actual constructor( albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg", downloaded = if (dir.isPresent( dir.finalOutputDir( - itemName = name, - type = folderType, - subFolder = subFolder, - defaultDir = dir.defaultDir() - ) + itemName = name, + type = folderType, + subFolder = subFolder, + defaultDir = dir.defaultDir() + ) ) ) DownloadStatus.Downloaded @@ -180,10 +180,10 @@ actual class YoutubeProvider actual constructor( title = name } catch (e: Exception) { e.printStackTrace() - logger.e{"An Error Occurred While Processing!,$searchId"} + logger.e { "An Error Occurred While Processing!,$searchId" } } } - return if(result.title.isNotBlank()) result + return if (result.title.isNotBlank()) result else null } -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/FileSave.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/FileSave.kt index e2a587ec..9256399a 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/FileSave.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/FileSave.kt @@ -39,4 +39,4 @@ external fun saveAs(data: String, filename: String = definedExternally) external fun saveAs(data: Blob, filename: String = definedExternally, disableAutoBOM: Boolean = definedExternally) -external fun saveAs(data: String, filename: String = definedExternally, disableAutoBOM: Boolean = definedExternally) \ No newline at end of file +external fun saveAs(data: String, filename: String = definedExternally, disableAutoBOM: Boolean = definedExternally) diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/ID3Writer.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/ID3Writer.kt index 679c8879..9fb0a376 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/ID3Writer.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/ID3Writer.kt @@ -22,10 +22,10 @@ import org.w3c.files.Blob @JsModule("browser-id3-writer") @JsNonModule external class ID3Writer(a: ArrayBuffer) { - fun setFrame(frameName:String,frameValue:Any):ID3Writer + fun setFrame(frameName: String, frameValue: Any): ID3Writer fun removeTag() - fun addTag():ArrayBuffer - fun getBlob():Blob - fun getURL():String + fun addTag(): ArrayBuffer + fun getBlob(): Blob + fun getURL(): String fun revokeURL() -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt index 5197b982..9500391c 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebActual.kt @@ -20,26 +20,28 @@ import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails -import io.ktor.client.request.* -import kotlinx.coroutines.* +import io.ktor.client.request.head +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.withContext -actual val currentPlatform:AllPlatforms = AllPlatforms.Js +actual val currentPlatform: AllPlatforms = AllPlatforms.Js -actual fun openPlatform(packageID:String, platformLink:String){ - //TODO +actual fun openPlatform(packageID: String, platformLink: String) { + // TODO } -actual fun shareApp(){ - //TODO +actual fun shareApp() { + // TODO } -actual fun giveDonation(){ - //TODO +actual fun giveDonation() { + // TODO } -actual fun queryActiveTracks(){} +actual fun queryActiveTracks() {} actual val dispatcherIO: CoroutineDispatcher = Dispatchers.Default @@ -58,8 +60,8 @@ private suspend fun isInternetAvailable(): Boolean { } } -actual val isInternetAvailable:Boolean - get(){ +actual val isInternetAvailable: Boolean + get() { return true /*var result = false val job = GlobalScope.launch { result = isInternetAvailable() } @@ -68,28 +70,28 @@ actual val isInternetAvailable:Boolean } val DownloadProgressFlow: MutableSharedFlow> = MutableSharedFlow(1) -//Error:https://github.com/Kotlin/kotlinx.atomicfu/issues/182 -//val DownloadScope = ParallelExecutor(Dispatchers.Default) //Download Pool of 4 parallel +// Error:https://github.com/Kotlin/kotlinx.atomicfu/issues/182 +// val DownloadScope = ParallelExecutor(Dispatchers.Default) //Download Pool of 4 parallel val allTracksStatus: HashMap = hashMapOf() actual suspend fun downloadTracks( list: List, fetcher: FetchPlatformQueryResult, dir: Dir -){ +) { list.forEach { - withContext(Dispatchers.Default) { + withContext(dispatcherIO) { allTracksStatus[it.title] = DownloadStatus.Queued - if (!it.videoID.isNullOrBlank()) {//Video ID already known! + if (!it.videoID.isNullOrBlank()) { // Video ID already known! downloadTrack(it.videoID!!, it, fetcher, dir) } else { val searchQuery = "${it.title} - ${it.artists.joinToString(",")}" - val videoID = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery,it) - println(videoID+" : "+it.title) + val videoID = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery, it) + println(videoID + " : " + it.title) if (videoID.isNullOrBlank()) { allTracksStatus[it.title] = DownloadStatus.Failed DownloadProgressFlow.emit(allTracksStatus) - } else {//Found Youtube Video ID + } else { // Found Youtube Video ID downloadTrack(videoID, it, fetcher, dir) } } @@ -98,18 +100,18 @@ actual suspend fun downloadTracks( } } -suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher:FetchPlatformQueryResult,dir:Dir) { +suspend fun downloadTrack(videoID: String, track: TrackDetails, fetcher: FetchPlatformQueryResult, dir: Dir) { val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID) - if(url == null){ + if (url == null) { allTracksStatus[track.title] = DownloadStatus.Failed DownloadProgressFlow.emit(allTracksStatus) println("No URL to Download") - }else { + } else { downloadFile(url).collect { - when(it){ + when (it) { is DownloadResult.Success -> { println("Download Completed") - dir.saveFileWithMetadata(it.byteArray, track) + dir.saveFileWithMetadata(it.byteArray, track) } is DownloadResult.Error -> { allTracksStatus[track.title] = DownloadStatus.Failed diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt index 4290ec90..fc0ed3c9 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebDir.kt @@ -27,8 +27,8 @@ import kotlinext.js.Object import kotlinext.js.js import kotlinx.coroutines.flow.collect import org.khronos.webgl.ArrayBuffer -import org.w3c.dom.ImageBitmap import org.khronos.webgl.Int8Array +import org.w3c.dom.ImageBitmap actual class Dir actual constructor( private val logger: Kermit, @@ -45,52 +45,52 @@ actual class Dir actual constructor( actual fun fileSeparator(): String = "/" actual fun imageCacheDir(): String = "TODO" + - fileSeparator() + "SpotiFlyer/.images" + fileSeparator() + fileSeparator() + "SpotiFlyer/.images" + fileSeparator() actual fun defaultDir(): String = "TODO" + fileSeparator() + - "SpotiFlyer" + fileSeparator() + "SpotiFlyer" + fileSeparator() actual fun isPresent(path: String): Boolean = false - actual fun createDirectory(dirPath:String){} + actual fun createDirectory(dirPath: String) {} actual suspend fun clearCache() {} - actual suspend fun cacheImage(image: Any,path:String) {} + actual suspend fun cacheImage(image: Any, path: String) {} @Suppress("BlockingMethodInNonBlockingContext") - actual suspend fun saveFileWithMetadata( - mp3ByteArray: ByteArray, - trackDetails: TrackDetails + actual suspend fun saveFileWithMetadata( + mp3ByteArray: ByteArray, + trackDetails: TrackDetails ) { val writer = ID3Writer(mp3ByteArray.toArrayBuffer()) - val albumArt = downloadFile(corsApi+trackDetails.albumArtURL) + val albumArt = downloadFile(corsApi + trackDetails.albumArtURL) albumArt.collect { - when(it){ + when (it) { is DownloadResult.Success -> { - logger.d{"Album Art Downloaded Success"} + logger.d { "Album Art Downloaded Success" } val albumArtObj = js { this["type"] = 3 this["data"] = it.byteArray.toArrayBuffer() this["description"] = "Cover Art" } - writeTagsAndSave(writer, albumArtObj as Object,trackDetails) + writeTagsAndSave(writer, albumArtObj as Object, trackDetails) } is DownloadResult.Error -> { - logger.d{"Album Art Downloading Error"} - writeTagsAndSave(writer,null,trackDetails) + logger.d { "Album Art Downloading Error" } + writeTagsAndSave(writer, null, trackDetails) } - is DownloadResult.Progress -> logger.d{"Album Art Downloading: ${it.progress}"} + is DownloadResult.Progress -> logger.d { "Album Art Downloading: ${it.progress}" } } } } - private suspend fun writeTagsAndSave(writer:ID3Writer, albumArt:Object?, trackDetails: TrackDetails){ + private suspend fun writeTagsAndSave(writer: ID3Writer, albumArt: Object?, trackDetails: TrackDetails) { writer.apply { setFrame("TIT2", trackDetails.title) setFrame("TPE1", trackDetails.artists.toTypedArray()) - setFrame("TALB", trackDetails.albumName?:"") - try{trackDetails.year?.substring(0,4)?.toInt()?.let { setFrame("TYER", it) }} catch(e:Exception){} + setFrame("TALB", trackDetails.albumName ?: "") + try { trackDetails.year?.substring(0, 4)?.toInt()?.let { setFrame("TYER", it) } } catch (e: Exception) {} setFrame("TPE2", trackDetails.artists.joinToString(",")) setFrame("WOAS", trackDetails.source.toString()) setFrame("TLEN", trackDetails.durationSec) @@ -102,7 +102,7 @@ actual class Dir actual constructor( saveAs(writer.getBlob(), "${removeIllegalChars(trackDetails.title)}.mp3") } - actual fun addToLibrary(path:String){} + actual fun addToLibrary(path: String) {} actual suspend fun loadImage(url: String): Picture { return Picture(url) @@ -110,12 +110,12 @@ actual class Dir actual constructor( private fun loadCachedImage(cachePath: String): ImageBitmap? = null - private suspend fun freshImage(url:String): ImageBitmap? = null + private suspend fun freshImage(url: String): ImageBitmap? = null actual val db: Database? get() = database } -fun ByteArray.toArrayBuffer():ArrayBuffer{ +fun ByteArray.toArrayBuffer(): ArrayBuffer { return this.unsafeCast().buffer -} \ No newline at end of file +} diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebPicture.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebPicture.kt index 872b8ca4..ba57748f 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebPicture.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/WebPicture.kt @@ -17,5 +17,5 @@ package com.shabinder.common.di actual data class Picture( - var imageUrl:String -) \ No newline at end of file + var imageUrl: String +) diff --git a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt index 2ccccb18..020178c1 100644 --- a/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt +++ b/common/dependency-injection/src/jsMain/kotlin/com/shabinder/common/di/YoutubeProvider.kt @@ -18,8 +18,7 @@ package com.shabinder.common.di import co.touchlab.kermit.Kermit import com.shabinder.common.models.PlatformQueryResult -import com.shabinder.database.Database -import io.ktor.client.* +import io.ktor.client.HttpClient actual class YoutubeProvider actual constructor( httpClient: HttpClient, @@ -29,4 +28,4 @@ actual class YoutubeProvider actual constructor( actual suspend fun query(fullLink: String): PlatformQueryResult? { return null // TODO } -} \ No newline at end of file +} diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt index 961a9f12..9dc3fb64 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt @@ -36,12 +36,12 @@ interface SpotiFlyerList { /* * Download All Tracks(after filtering already Downloaded) * */ - fun onDownloadAllClicked(trackList:List) + fun onDownloadAllClicked(trackList: List) /* * Download All Tracks(after filtering already Downloaded) * */ - fun onDownloadClicked(track:TrackDetails) + fun onDownloadClicked(track: TrackDetails) /* * To Pop and return back to Main Screen @@ -51,7 +51,7 @@ interface SpotiFlyerList { /* * Load Image from cache/Internet and cache it * */ - suspend fun loadImage(url:String): Picture + suspend fun loadImage(url: String): Picture /* * Sync Tracks Statuses @@ -64,7 +64,7 @@ interface SpotiFlyerList { val dir: Dir val link: String val listOutput: Consumer - val showPopUpMessage:(String)->Unit + val showPopUpMessage: (String) -> Unit val downloadProgressFlow: MutableSharedFlow> } sealed class Output { @@ -72,8 +72,8 @@ interface SpotiFlyerList { } data class State( val queryResult: PlatformQueryResult? = null, - val link:String = "", - val trackList:List = emptyList() + val link: String = "", + val trackList: List = emptyList() ) } diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt index 7683d1e3..06f82165 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.Flow internal class SpotiFlyerListImpl( componentContext: ComponentContext, dependencies: Dependencies -): SpotiFlyerList,ComponentContext by componentContext, Dependencies by dependencies { +) : SpotiFlyerList, ComponentContext by componentContext, Dependencies by dependencies { private val store = instanceKeeper.getStore { @@ -51,11 +51,11 @@ internal class SpotiFlyerListImpl( store.accept(Intent.StartDownloadAll(trackList)) } - override fun onDownloadClicked(track:TrackDetails) { + override fun onDownloadClicked(track: TrackDetails) { store.accept(Intent.StartDownload(track)) } - override fun onBackPressed(){ + override fun onBackPressed() { listOutput.callback(SpotiFlyerList.Output.Finished) } @@ -64,4 +64,4 @@ internal class SpotiFlyerListImpl( } override suspend fun loadImage(url: String): Picture = dir.loadImage(url) -} \ No newline at end of file +} diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/InstanceKeeperExt.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/InstanceKeeperExt.kt index a9e80e66..bb2c9a3d 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/InstanceKeeperExt.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/InstanceKeeperExt.kt @@ -24,9 +24,9 @@ fun > InstanceKeeper.getStore(key: Any, factory: () -> T): T getOrCreate(key) { StoreHolder(factory()) } .store -inline fun > InstanceKeeper.getStore(noinline factory: () -> T): T = + Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T = getStore(T::class, factory) private class StoreHolder>( diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStore.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStore.kt index 826eab9d..b0da0379 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStore.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStore.kt @@ -21,11 +21,11 @@ import com.shabinder.common.list.SpotiFlyerList.State import com.shabinder.common.list.store.SpotiFlyerListStore.Intent import com.shabinder.common.models.TrackDetails -internal interface SpotiFlyerListStore: Store { +internal interface SpotiFlyerListStore : Store { sealed class Intent { - data class SearchLink(val link: String): Intent() - data class StartDownload(val track:TrackDetails): Intent() - data class StartDownloadAll(val trackList: List): Intent() - object RefreshTracksStatuses: Intent() + data class SearchLink(val link: String) : Intent() + data class StartDownload(val track: TrackDetails) : Intent() + data class StartDownloadAll(val trackList: List) : Intent() + object RefreshTracksStatuses : Intent() } } diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt index 31c8c4f7..0fb132f5 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt @@ -16,10 +16,16 @@ package com.shabinder.common.list.store -import com.arkivanov.mvikotlin.core.store.* +import com.arkivanov.mvikotlin.core.store.Reducer +import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper +import com.arkivanov.mvikotlin.core.store.Store +import com.arkivanov.mvikotlin.core.store.StoreFactory import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor import com.shabinder.common.database.getLogger -import com.shabinder.common.di.* +import com.shabinder.common.di.Dir +import com.shabinder.common.di.FetchPlatformQueryResult +import com.shabinder.common.di.downloadTracks +import com.shabinder.common.di.queryActiveTracks import com.shabinder.common.list.SpotiFlyerList.State import com.shabinder.common.list.store.SpotiFlyerListStore.Intent import com.shabinder.common.models.DownloadStatus @@ -38,55 +44,57 @@ internal class SpotiFlyerListStoreProvider( ) { val logger = getLogger() fun provide(): SpotiFlyerListStore = - object : SpotiFlyerListStore, Store by storeFactory.create( - name = "SpotiFlyerListStore", - initialState = State(), - bootstrapper = SimpleBootstrapper(Unit), - executorFactory = ::ExecutorImpl, - reducer = ReducerImpl - ) {} + object : + SpotiFlyerListStore, + Store by storeFactory.create( + name = "SpotiFlyerListStore", + initialState = State(), + bootstrapper = SimpleBootstrapper(Unit), + executorFactory = ::ExecutorImpl, + reducer = ReducerImpl + ) {} private sealed class Result { - data class ResultFetched(val result: PlatformQueryResult,val trackList: List) : Result() - data class UpdateTrackList(val list:List): Result() - data class UpdateTrackItem(val item:TrackDetails): Result() + data class ResultFetched(val result: PlatformQueryResult, val trackList: List) : Result() + data class UpdateTrackList(val list: List) : Result() + data class UpdateTrackItem(val item: TrackDetails) : Result() } - private inner class ExecutorImpl: SuspendExecutor() { + private inner class ExecutorImpl : SuspendExecutor() { override suspend fun executeAction(action: Unit, getState: () -> State) { - executeIntent(Intent.SearchLink(link),getState) + executeIntent(Intent.SearchLink(link), getState) downloadProgressFlow.collectLatest { map -> - logger.d(map.size.toString(),"ListStore: flow Updated") + logger.d(map.size.toString(), "ListStore: flow Updated") val updatedTrackList = getState().trackList.updateTracksStatuses(map) - if(updatedTrackList.isNotEmpty()) dispatch(Result.UpdateTrackList(updatedTrackList)) + if (updatedTrackList.isNotEmpty()) dispatch(Result.UpdateTrackList(updatedTrackList)) } } override suspend fun executeIntent(intent: Intent, getState: () -> State) { when (intent) { - is Intent.SearchLink -> fetchQuery.query(link)?.let{ result -> + is Intent.SearchLink -> fetchQuery.query(link)?.let { result -> result.trackList = result.trackList.toMutableList() - dispatch((Result.ResultFetched(result,result.trackList.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0){ hashMapOf()})))) - executeIntent(Intent.RefreshTracksStatuses,getState) + dispatch((Result.ResultFetched(result, result.trackList.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0) { hashMapOf() })))) + executeIntent(Intent.RefreshTracksStatuses, getState) } is Intent.StartDownloadAll -> { val finalList = intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded } - if (finalList.isNullOrEmpty()) showPopUpMessage("All Songs are Processed") - else downloadTracks(finalList,fetchQuery,dir) + if (finalList.isNullOrEmpty()) showPopUpMessage("All Songs are Processed") + else downloadTracks(finalList, fetchQuery, dir) val list = intent.trackList.map { if (it.downloaded == DownloadStatus.NotDownloaded) return@map it.copy(downloaded = DownloadStatus.Queued) it } - dispatch(Result.UpdateTrackList(list.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0){ hashMapOf()}))) + dispatch(Result.UpdateTrackList(list.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0) { hashMapOf() }))) } is Intent.StartDownload -> { - downloadTracks(listOf(intent.track),fetchQuery,dir) + downloadTracks(listOf(intent.track), fetchQuery, dir) dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued))) } is Intent.RefreshTracksStatuses -> queryActiveTracks() @@ -95,34 +103,34 @@ internal class SpotiFlyerListStoreProvider( } private object ReducerImpl : Reducer { override fun State.reduce(result: Result): State = - when (result) { - is Result.ResultFetched -> copy(queryResult = result.result, trackList = result.trackList ,link = link) - is Result.UpdateTrackList -> copy(trackList = result.list) - is Result.UpdateTrackItem -> updateTrackItem(result.item) - } + when (result) { + is Result.ResultFetched -> copy(queryResult = result.result, trackList = result.trackList, link = link) + is Result.UpdateTrackList -> copy(trackList = result.list) + is Result.UpdateTrackItem -> updateTrackItem(result.item) + } - private fun State.updateTrackItem(item: TrackDetails):State{ + private fun State.updateTrackItem(item: TrackDetails): State { val position = this.trackList.map { it.title }.indexOf(item.title) - if(position != -1){ - return copy(trackList = trackList.toMutableList().apply { set(position,item) }) + if (position != -1) { + return copy(trackList = trackList.toMutableList().apply { set(position, item) }) } return this } } - private fun List.updateTracksStatuses(map:HashMap):List{ + private fun List.updateTracksStatuses(map: HashMap): List { val titleList = this.map { it.title } val updatedList = mutableListOf().also { it.addAll(this) } - for(newTrack in map){ + for (newTrack in map) { titleList.indexOf(newTrack.key).let { position -> - if(position != -1){ - updatedList.getOrNull(position)?.copy(downloaded = newTrack.value,progress = (newTrack.value as? DownloadStatus.Downloading)?.progress ?: updatedList[position].progress )?.also { updatedTrack -> + if (position != -1) { + updatedList.getOrNull(position)?.copy(downloaded = newTrack.value, progress = (newTrack.value as? DownloadStatus.Downloading)?.progress ?: updatedList[position].progress)?.also { updatedTrack -> updatedList[position] = updatedTrack - //logger.d("$position) ${updatedTrack.downloaded} - ${updatedTrack.title}","List Store Track Update") + // logger.d("$position) ${updatedTrack.downloaded} - ${updatedTrack.title}","List Store Track Update") } } } } return updatedList } -} \ No newline at end of file +} diff --git a/common/main/build.gradle.kts b/common/main/build.gradle.kts index 455cf73f..c0ade5f4 100644 --- a/common/main/build.gradle.kts +++ b/common/main/build.gradle.kts @@ -14,8 +14,6 @@ * * along with this program. If not, see . */ -import org.jetbrains.compose.compose - plugins { id("multiplatform-setup") id("android-setup") diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt index bb647288..4d52c78a 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt @@ -49,14 +49,14 @@ interface SpotiFlyerMain { /* * Load Image from cache/Internet and cache it * */ - suspend fun loadImage(url:String): Picture + suspend fun loadImage(url: String): Picture interface Dependencies { val mainOutput: Consumer val storeFactory: StoreFactory val database: Database? val dir: Dir - val showPopUpMessage:(String)->Unit + val showPopUpMessage: (String) -> Unit } sealed class Output { diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt index d81548c5..9a34dccb 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt @@ -21,7 +21,10 @@ import com.arkivanov.mvikotlin.extensions.coroutines.states import com.shabinder.common.di.Picture import com.shabinder.common.di.isInternetAvailable import com.shabinder.common.main.SpotiFlyerMain -import com.shabinder.common.main.SpotiFlyerMain.* +import com.shabinder.common.main.SpotiFlyerMain.Dependencies +import com.shabinder.common.main.SpotiFlyerMain.HomeCategory +import com.shabinder.common.main.SpotiFlyerMain.Output +import com.shabinder.common.main.SpotiFlyerMain.State import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider import com.shabinder.common.main.store.getStore @@ -30,7 +33,7 @@ import kotlinx.coroutines.flow.Flow internal class SpotiFlyerMainImpl( componentContext: ComponentContext, dependencies: Dependencies -): SpotiFlyerMain,ComponentContext by componentContext, Dependencies by dependencies { +) : SpotiFlyerMain, ComponentContext by componentContext, Dependencies by dependencies { private val store = instanceKeeper.getStore { @@ -44,7 +47,7 @@ internal class SpotiFlyerMainImpl( override val models: Flow = store.states override fun onLinkSearch(link: String) { - if(isInternetAvailable) mainOutput.callback(Output.Search(link = link)) + if (isInternetAvailable) mainOutput.callback(Output.Search(link = link)) else showPopUpMessage("Check Network Connection Please") } @@ -57,4 +60,4 @@ internal class SpotiFlyerMainImpl( } override suspend fun loadImage(url: String): Picture = dir.loadImage(url) -} \ No newline at end of file +} diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/InstanceKeeperExt.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/InstanceKeeperExt.kt index 55e1f507..7ac77488 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/InstanceKeeperExt.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/InstanceKeeperExt.kt @@ -24,9 +24,9 @@ fun > InstanceKeeper.getStore(key: Any, factory: () -> T): T getOrCreate(key) { StoreHolder(factory()) } .store -inline fun > InstanceKeeper.getStore(noinline factory: () -> T): T = + Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T = getStore(T::class, factory) private class StoreHolder>( diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt index d9998292..f499f11d 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt @@ -20,12 +20,12 @@ import com.arkivanov.mvikotlin.core.store.Store import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent -internal interface SpotiFlyerMainStore: Store { +internal interface SpotiFlyerMainStore : Store { sealed class Intent { - data class OpenPlatform(val platformID:String,val platformLink:String):Intent() - data class SetLink(val link:String):Intent() - data class SelectCategory(val category: SpotiFlyerMain.HomeCategory):Intent() + data class OpenPlatform(val platformID: String, val platformLink: String) : Intent() + data class SetLink(val link: String) : Intent() + data class SelectCategory(val category: SpotiFlyerMain.HomeCategory) : Intent() object GiveDonation : Intent() - object ShareApp: Intent() + object ShareApp : Intent() } } diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStoreProvider.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStoreProvider.kt index 7152e2c4..356d404b 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStoreProvider.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStoreProvider.kt @@ -38,18 +38,20 @@ import kotlinx.coroutines.flow.map internal class SpotiFlyerMainStoreProvider( private val storeFactory: StoreFactory, - private val showPopUpMessage: (String)->Unit, + private val showPopUpMessage: (String) -> Unit, private val database: Database? ) { fun provide(): SpotiFlyerMainStore = - object : SpotiFlyerMainStore, Store by storeFactory.create( - name = "SpotiFlyerHomeStore", - initialState = State(), - bootstrapper = SimpleBootstrapper(Unit), - executorFactory = ::ExecutorImpl, - reducer = ReducerImpl - ) {} + object : + SpotiFlyerMainStore, + Store by storeFactory.create( + name = "SpotiFlyerHomeStore", + initialState = State(), + bootstrapper = SimpleBootstrapper(Unit), + executorFactory = ::ExecutorImpl, + reducer = ReducerImpl + ) {} val updates: Flow>? = database?.downloadRecordDatabaseQueries @@ -58,13 +60,12 @@ internal class SpotiFlyerMainStoreProvider( ?.mapToList(Dispatchers.Default) ?.map { it.map { record -> - record.run{ + record.run { DownloadRecord(id, type, name, link, coverUrl, totalFiles) } } } - private sealed class Result { data class ItemsLoaded(val items: List) : Result() data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result() @@ -91,10 +92,10 @@ internal class SpotiFlyerMainStoreProvider( private object ReducerImpl : Reducer { override fun State.reduce(result: Result): State = - when (result) { + when (result) { is Result.ItemsLoaded -> copy(records = result.items) is Result.LinkChanged -> copy(link = result.link) is Result.CategoryChanged -> copy(selectedCategory = result.category) - } + } } -} \ No newline at end of file +} diff --git a/common/root/build.gradle.kts b/common/root/build.gradle.kts index 9dc0a5bc..08c8f86c 100644 --- a/common/root/build.gradle.kts +++ b/common/root/build.gradle.kts @@ -14,8 +14,6 @@ * * along with this program. If not, see . */ -import org.jetbrains.compose.compose - plugins { id("multiplatform-setup") id("android-setup") diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt index 471dc67b..7277ef82 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt @@ -47,7 +47,7 @@ interface SpotiFlyerRoot { val database: Database? val fetchPlatformQueryResult: FetchPlatformQueryResult val directories: Dir - val showPopUpMessage:(String)->Unit + val showPopUpMessage: (String) -> Unit val downloadProgressReport: MutableSharedFlow> } } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt index f89215ad..5183b950 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt @@ -17,6 +17,6 @@ package com.shabinder.common.root.callbacks interface SpotiFlyerRootCallBacks { - fun searchLink(link:String) + fun searchLink(link: String) fun popBackToHomeScreen() } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt index 1be3883f..e7de5f01 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt @@ -16,7 +16,12 @@ package com.shabinder.common.root.integration -import com.arkivanov.decompose.* +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.RouterState +import com.arkivanov.decompose.pop +import com.arkivanov.decompose.popWhile +import com.arkivanov.decompose.push +import com.arkivanov.decompose.router import com.arkivanov.decompose.statekeeper.Parcelable import com.arkivanov.decompose.statekeeper.Parcelize import com.arkivanov.decompose.value.Value @@ -43,7 +48,7 @@ internal class SpotiFlyerRootImpl( override val routerState: Value> = router.state - override val callBacks = object : SpotiFlyerRootCallBacks{ + override val callBacks = object : SpotiFlyerRootCallBacks { override fun searchLink(link: String) = onMainOutput(SpotiFlyerMain.Output.Search(link)) override fun popBackToHomeScreen() { router.popWhile { @@ -74,7 +79,7 @@ internal class SpotiFlyerRootImpl( override val fetchQuery = fetchPlatformQueryResult override val dir: Dir = directories override val link: String = link - override val listOutput : Consumer = Consumer(::onListOutput) + override val listOutput: Consumer = Consumer(::onListOutput) override val downloadProgressFlow = downloadProgressReport } ) diff --git a/desktop/src/jvmMain/kotlin/Main.kt b/desktop/src/jvmMain/kotlin/Main.kt index 713787ae..f30de8e7 100644 --- a/desktop/src/jvmMain/kotlin/Main.kt +++ b/desktop/src/jvmMain/kotlin/Main.kt @@ -18,7 +18,6 @@ import androidx.compose.desktop.DesktopMaterialTheme import androidx.compose.desktop.Window import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Surface -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.arkivanov.decompose.ComponentContext @@ -31,14 +30,17 @@ import com.shabinder.common.di.DownloadProgressFlow import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.initKoin import com.shabinder.common.root.SpotiFlyerRoot -import com.shabinder.common.uikit.* +import com.shabinder.common.uikit.SpotiFlyerColors +import com.shabinder.common.uikit.SpotiFlyerRootContent +import com.shabinder.common.uikit.SpotiFlyerShapes +import com.shabinder.common.uikit.SpotiFlyerTypography +import com.shabinder.common.uikit.colorOffWhite import com.shabinder.database.Database import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage private val koin = initKoin(enableNetworkLogs = true).koin - -fun main(){ +fun main() { val lifecycle = LifecycleRegistry() lifecycle.resume() @@ -54,7 +56,7 @@ fun main(){ typography = SpotiFlyerTypography, shapes = SpotiFlyerShapes ) { - val callBacks = SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot)).callBacks + SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot)) } } } @@ -71,4 +73,4 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot = override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage override val downloadProgressReport = DownloadProgressFlow } - ) \ No newline at end of file + ) diff --git a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/ToStringFunction.kt b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/ToStringFunction.kt index d44f9c1c..d38d4563 100644 --- a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/ToStringFunction.kt +++ b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/ToStringFunction.kt @@ -16,7 +16,6 @@ package com.willowtreeapps.fuzzywuzzy - /** * Transforms an item of type T to a String. * diff --git a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/DiffUtils.kt b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/DiffUtils.kt index 2f3cd60b..d32fb835 100644 --- a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/DiffUtils.kt +++ b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/DiffUtils.kt @@ -18,7 +18,10 @@ package com.willowtreeapps.fuzzywuzzy.diffutils import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditOp import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType -import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType.* +import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType.DELETE +import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType.INSERT +import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType.KEEP +import com.willowtreeapps.fuzzywuzzy.diffutils.structs.EditType.REPLACE import com.willowtreeapps.fuzzywuzzy.diffutils.structs.MatchingBlock import com.willowtreeapps.fuzzywuzzy.diffutils.structs.OpCode @@ -39,18 +42,12 @@ object DiffUtils { var len2Copy = len2 var len1o = 0 - val len2o: Int var i = 0 - val matrix: IntArray - - val c1 = s1 - val c2 = s2 - var p1 = 0 var p2 = 0 - while (len1Copy > 0 && len2Copy > 0 && c1[p1] == c2[p2]) { + while (len1Copy > 0 && len2Copy > 0 && s1[p1] == s2[p2]) { len1Copy-- len2Copy-- @@ -60,10 +57,10 @@ object DiffUtils { len1o++ } - len2o = len1o + val len2o: Int = len1o /* strip common suffix */ - while (len1Copy > 0 && len2Copy > 0 && c1[p1 + len1Copy - 1] == c2[p2 + len2Copy - 1]) { + while (len1Copy > 0 && len2Copy > 0 && s1[p1 + len1Copy - 1] == s2[p2 + len2Copy - 1]) { len1Copy-- len2Copy-- } @@ -71,7 +68,7 @@ object DiffUtils { len1Copy++ len2Copy++ - matrix = IntArray(len2Copy * len1Copy) + val matrix: IntArray = IntArray(len2Copy * len1Copy) while (i < len2Copy) { matrix[i] = i @@ -90,7 +87,7 @@ object DiffUtils { var ptrC = i * len2Copy val ptrEnd = ptrC + len2Copy - 1 - val char1 = c1[p1 + i - 1] + val char1 = s1[p1 + i - 1] var ptrChar2 = p2 var x = i @@ -99,7 +96,7 @@ object DiffUtils { while (ptrC <= ptrEnd) { - var c3 = matrix[ptrPrev++] + if (char1 != c2[ptrChar2++]) 1 else 0 + var c3 = matrix[ptrPrev++] + if (char1 != s2[ptrChar2++]) 1 else 0 x++ if (x > c3) { @@ -113,14 +110,12 @@ object DiffUtils { } matrix[ptrC++] = x - } i++ - } - return editOpsFromCostMatrix(len1Copy, c1, p1, len1o, len2Copy, c2, p2, len2o, matrix) + return editOpsFromCostMatrix(len1Copy, s1, p1, len1o, len2Copy, s2, p2, len2o, matrix) } @@ -134,11 +129,9 @@ object DiffUtils { var ptr: Int = len1 * len2 - 1 - val ops: Array - var dir = 0 - ops = arrayOfNulls(pos) + val ops: Array = arrayOfNulls(pos) while (i > 0 || j > 0) { @@ -246,10 +239,9 @@ object DiffUtils { val n = ops.size var noOfMB = 0 - var i: Int var o = 0 - i = n + var i: Int = n while (i-- != 0) { if (ops[o].type === KEEP) { @@ -324,7 +316,6 @@ object DiffUtils { val n = ops.size var numberOfMatchingBlocks = 0 - var i: Int var spos: Int var dpos: Int @@ -335,7 +326,7 @@ object DiffUtils { var type: EditType - i = n + var i: Int = n while (i != 0) { diff --git a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/FuzzySearch.kt b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/FuzzySearch.kt index a9cee4c1..8612d7aa 100644 --- a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/FuzzySearch.kt +++ b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/FuzzySearch.kt @@ -28,6 +28,7 @@ import com.willowtreeapps.fuzzywuzzy.diffutils.ratio.SimpleRatio /** * FuzzySearch facade class */ +@Suppress("unused") object FuzzySearch { /** diff --git a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/BasicAlgorithm.kt b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/BasicAlgorithm.kt index c35f54c1..8c82d2f8 100644 --- a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/BasicAlgorithm.kt +++ b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/BasicAlgorithm.kt @@ -19,7 +19,6 @@ package com.willowtreeapps.fuzzywuzzy.diffutils.algorithms import com.willowtreeapps.fuzzywuzzy.ToStringFunction import com.willowtreeapps.fuzzywuzzy.diffutils.Applicable - abstract class BasicAlgorithm : Applicable { var stringFunction: ToStringFunction? = null diff --git a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/WeightedRatio.kt b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/WeightedRatio.kt index 378ef7f9..7ad6d0ec 100644 --- a/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/WeightedRatio.kt +++ b/fuzzywuzzy/app/src/commonMain/kotlin/com/willowtreeapps/fuzzywuzzy/diffutils/algorithms/WeightedRatio.kt @@ -27,7 +27,6 @@ import kotlin.math.max import kotlin.math.min import kotlin.math.round - class WeightedRatio : BasicAlgorithm() { diff --git a/web-app/src/main/kotlin/App.kt b/web-app/src/main/kotlin/App.kt index d8e11acb..9a8950c8 100644 --- a/web-app/src/main/kotlin/App.kt +++ b/web-app/src/main/kotlin/App.kt @@ -26,7 +26,11 @@ import com.shabinder.common.di.DownloadProgressFlow import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.database.Database import extras.renderableChild -import react.* +import react.RBuilder +import react.RComponent +import react.RProps +import react.RState +import react.ReactElement import root.RootR external interface AppProps : RProps { @@ -45,8 +49,6 @@ class App(props: AppProps): RComponent(props) { private val lifecycle = LifecycleRegistry() private val ctx = DefaultComponentContext(lifecycle = lifecycle) private val dependencies = props.dependencies - private val logger:Kermit - get() = dependencies.logger private val root = SpotiFlyerRoot(ctx, object : SpotiFlyerRoot.Dependencies{ diff --git a/web-app/src/main/kotlin/Styles.kt b/web-app/src/main/kotlin/Styles.kt index fbc28b0c..e93a7569 100644 --- a/web-app/src/main/kotlin/Styles.kt +++ b/web-app/src/main/kotlin/Styles.kt @@ -14,10 +14,28 @@ * * along with this program. If not, see . */ -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.BorderStyle +import kotlinx.css.Color +import kotlinx.css.Display +import kotlinx.css.JustifyContent +import kotlinx.css.alignContent +import kotlinx.css.alignItems +import kotlinx.css.backgroundColor +import kotlinx.css.borderBottomColor +import kotlinx.css.borderBottomStyle +import kotlinx.css.borderColor +import kotlinx.css.borderRadius +import kotlinx.css.borderRightColor +import kotlinx.css.borderWidth +import kotlinx.css.color +import kotlinx.css.display +import kotlinx.css.justifyContent +import kotlinx.css.margin +import kotlinx.css.padding +import kotlinx.css.px import styled.StyleSheet - val colorPrimary = Color("#FC5C7D") val colorPrimaryDark = Color("#CE1CFF") val colorAccent = Color("#9AB3FF") diff --git a/web-app/src/main/kotlin/extras/RenderableComponent.kt b/web-app/src/main/kotlin/extras/RenderableComponent.kt index 6d48693e..e6833f9b 100644 --- a/web-app/src/main/kotlin/extras/RenderableComponent.kt +++ b/web-app/src/main/kotlin/extras/RenderableComponent.kt @@ -16,9 +16,13 @@ package extras -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import react.RComponent import react.RProps import react.RState diff --git a/web-app/src/main/kotlin/extras/RenderableRootComponent.kt b/web-app/src/main/kotlin/extras/RenderableRootComponent.kt index 8181c644..e687c9ff 100644 --- a/web-app/src/main/kotlin/extras/RenderableRootComponent.kt +++ b/web-app/src/main/kotlin/extras/RenderableRootComponent.kt @@ -19,7 +19,10 @@ package extras import com.arkivanov.decompose.value.Value import com.arkivanov.decompose.value.ValueObserver import extras.RenderableRootComponent.Props -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.isActive import react.RComponent import react.RProps import react.RState diff --git a/web-app/src/main/kotlin/home/HomeScreen.kt b/web-app/src/main/kotlin/home/HomeScreen.kt index 658b6227..dc75c1f9 100644 --- a/web-app/src/main/kotlin/home/HomeScreen.kt +++ b/web-app/src/main/kotlin/home/HomeScreen.kt @@ -16,16 +16,22 @@ package home -import com.shabinder.common.di.currentPlatform import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain.State -import com.shabinder.common.models.AllPlatforms import extras.RenderableComponent import kotlinx.browser.document import kotlinx.coroutines.flow.Flow -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.FlexDirection +import kotlinx.css.JustifyContent +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.flexDirection +import kotlinx.css.flexGrow +import kotlinx.css.justifyContent import kotlinx.dom.appendElement -import react.* +import react.RBuilder import styled.css import styled.styledDiv diff --git a/web-app/src/main/kotlin/home/IconList.kt b/web-app/src/main/kotlin/home/IconList.kt index 09b43509..2f757ad2 100644 --- a/web-app/src/main/kotlin/home/IconList.kt +++ b/web-app/src/main/kotlin/home/IconList.kt @@ -16,14 +16,23 @@ package home -import kotlinx.browser.document -import kotlinx.css.* -import kotlinx.dom.appendElement -import kotlinx.dom.createElement -import kotlinx.html.SCRIPT +import Styles +import kotlinx.css.borderRadius +import kotlinx.css.height +import kotlinx.css.margin +import kotlinx.css.px +import kotlinx.css.width import kotlinx.html.id -import react.* -import styled.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent +import styled.css +import styled.styledA +import styled.styledDiv +import styled.styledForm +import styled.styledImg external interface IconListProps : RProps { var iconsAndPlatforms: Map diff --git a/web-app/src/main/kotlin/home/Message.kt b/web-app/src/main/kotlin/home/Message.kt index cc73bd17..4896de97 100644 --- a/web-app/src/main/kotlin/home/Message.kt +++ b/web-app/src/main/kotlin/home/Message.kt @@ -16,8 +16,13 @@ package home -import kotlinx.css.* -import react.* +import kotlinx.css.em +import kotlinx.css.fontSize +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv import styled.styledH1 diff --git a/web-app/src/main/kotlin/home/Searchbar.kt b/web-app/src/main/kotlin/home/Searchbar.kt index ccfb89ce..ce7d8a55 100644 --- a/web-app/src/main/kotlin/home/Searchbar.kt +++ b/web-app/src/main/kotlin/home/Searchbar.kt @@ -22,9 +22,15 @@ import kotlinx.html.js.onChangeFunction import kotlinx.html.js.onClickFunction import kotlinx.html.js.onKeyDownFunction import org.w3c.dom.HTMLInputElement -import org.w3c.dom.Window -import react.* -import styled.* +import react.RBuilder +import react.RProps +import react.child +import react.functionalComponent +import styled.css +import styled.styledButton +import styled.styledDiv +import styled.styledImg +import styled.styledInput external interface SearchbarProps : RProps { var link: String diff --git a/web-app/src/main/kotlin/list/CircularProgressBar.kt b/web-app/src/main/kotlin/list/CircularProgressBar.kt index d131fd09..165a072d 100644 --- a/web-app/src/main/kotlin/list/CircularProgressBar.kt +++ b/web-app/src/main/kotlin/list/CircularProgressBar.kt @@ -16,8 +16,18 @@ package list -import kotlinx.css.* -import react.* +import kotlinx.css.Display +import kotlinx.css.JustifyContent +import kotlinx.css.display +import kotlinx.css.justifyContent +import kotlinx.css.marginBottom +import kotlinx.css.px +import kotlinx.css.width +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv import styled.styledSpan diff --git a/web-app/src/main/kotlin/list/CoverImage.kt b/web-app/src/main/kotlin/list/CoverImage.kt index ebe26a64..793df092 100644 --- a/web-app/src/main/kotlin/list/CoverImage.kt +++ b/web-app/src/main/kotlin/list/CoverImage.kt @@ -16,15 +16,29 @@ package list -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.FlexDirection +import kotlinx.css.TextAlign +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.flexDirection +import kotlinx.css.height +import kotlinx.css.marginTop +import kotlinx.css.px +import kotlinx.css.textAlign +import kotlinx.css.width import kotlinx.html.id -import react.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv import styled.styledH1 import styled.styledImg - external interface CoverImageProps : RProps { var coverImageURL: String var coverName: String diff --git a/web-app/src/main/kotlin/list/DownloadAllButton.kt b/web-app/src/main/kotlin/list/DownloadAllButton.kt index ff14fd82..fc1c2f11 100644 --- a/web-app/src/main/kotlin/list/DownloadAllButton.kt +++ b/web-app/src/main/kotlin/list/DownloadAllButton.kt @@ -16,10 +16,26 @@ package list -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.JustifyContent +import kotlinx.css.WhiteSpace +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.fontSize +import kotlinx.css.height +import kotlinx.css.justifyContent +import kotlinx.css.px +import kotlinx.css.whiteSpace import kotlinx.html.id import kotlinx.html.js.onClickFunction -import react.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent +import react.useEffect +import react.useState import styled.css import styled.styledDiv import styled.styledH5 diff --git a/web-app/src/main/kotlin/list/DownloadButton.kt b/web-app/src/main/kotlin/list/DownloadButton.kt index 18ba10da..5d576c30 100644 --- a/web-app/src/main/kotlin/list/DownloadButton.kt +++ b/web-app/src/main/kotlin/list/DownloadButton.kt @@ -17,9 +17,17 @@ package list import com.shabinder.common.models.DownloadStatus -import kotlinx.css.* +import kotlinx.css.borderRadius +import kotlinx.css.em +import kotlinx.css.margin +import kotlinx.css.px +import kotlinx.css.width import kotlinx.html.js.onClickFunction -import react.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv import styled.styledImg diff --git a/web-app/src/main/kotlin/list/ListScreen.kt b/web-app/src/main/kotlin/list/ListScreen.kt index 10eda1d5..34c248f6 100644 --- a/web-app/src/main/kotlin/list/ListScreen.kt +++ b/web-app/src/main/kotlin/list/ListScreen.kt @@ -20,7 +20,15 @@ import com.shabinder.common.list.SpotiFlyerList import com.shabinder.common.list.SpotiFlyerList.State import extras.RenderableComponent import kotlinx.coroutines.flow.Flow -import kotlinx.css.* +import kotlinx.css.Color +import kotlinx.css.Display +import kotlinx.css.FlexDirection +import kotlinx.css.color +import kotlinx.css.display +import kotlinx.css.flexDirection +import kotlinx.css.flexGrow +import kotlinx.css.padding +import kotlinx.css.px import kotlinx.html.id import react.RBuilder import styled.css @@ -65,7 +73,7 @@ class ListScreen( flexDirection = FlexDirection.column color = Color.white } - state.data.trackList.forEachIndexed{ index, trackDetails -> + state.data.trackList.forEachIndexed{ _, trackDetails -> TrackItem { details = trackDetails downloadTrack = model::onDownloadClicked diff --git a/web-app/src/main/kotlin/list/LoadingAnim.kt b/web-app/src/main/kotlin/list/LoadingAnim.kt index 897885f5..f618c280 100644 --- a/web-app/src/main/kotlin/list/LoadingAnim.kt +++ b/web-app/src/main/kotlin/list/LoadingAnim.kt @@ -16,12 +16,22 @@ package list -import kotlinx.css.* -import react.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.flexGrow +import kotlinx.css.height +import kotlinx.css.px +import kotlinx.css.width +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv - @Suppress("FunctionName") fun RBuilder.LoadingAnim(handler: RProps.() -> Unit): ReactElement { return child(loadingAnim){ diff --git a/web-app/src/main/kotlin/list/LoadingSpinner.kt b/web-app/src/main/kotlin/list/LoadingSpinner.kt index 72fdea3a..a03cb107 100644 --- a/web-app/src/main/kotlin/list/LoadingSpinner.kt +++ b/web-app/src/main/kotlin/list/LoadingSpinner.kt @@ -19,7 +19,11 @@ package list import kotlinx.css.marginRight import kotlinx.css.px import kotlinx.css.width -import react.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent import styled.css import styled.styledDiv diff --git a/web-app/src/main/kotlin/list/TrackItem.kt b/web-app/src/main/kotlin/list/TrackItem.kt index d6c5062b..c9938a19 100644 --- a/web-app/src/main/kotlin/list/TrackItem.kt +++ b/web-app/src/main/kotlin/list/TrackItem.kt @@ -18,10 +18,43 @@ package list import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.FlexDirection +import kotlinx.css.Overflow +import kotlinx.css.TextAlign +import kotlinx.css.TextOverflow +import kotlinx.css.WhiteSpace +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.em +import kotlinx.css.flexDirection +import kotlinx.css.flexGrow +import kotlinx.css.fontSize +import kotlinx.css.height +import kotlinx.css.margin +import kotlinx.css.minWidth +import kotlinx.css.overflow +import kotlinx.css.padding +import kotlinx.css.paddingRight +import kotlinx.css.px +import kotlinx.css.textAlign +import kotlinx.css.textOverflow +import kotlinx.css.whiteSpace +import kotlinx.css.width import kotlinx.html.id -import react.* -import styled.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent +import react.useEffect +import react.useState +import styled.css +import styled.styledDiv +import styled.styledH3 +import styled.styledH4 +import styled.styledImg external interface TrackItemProps : RProps { var details:TrackDetails diff --git a/web-app/src/main/kotlin/navbar/NavBar.kt b/web-app/src/main/kotlin/navbar/NavBar.kt index d96e2e01..cfa9f7c8 100644 --- a/web-app/src/main/kotlin/navbar/NavBar.kt +++ b/web-app/src/main/kotlin/navbar/NavBar.kt @@ -16,12 +16,33 @@ package navbar -import kotlinx.css.* +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.LinearDimension +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.filter +import kotlinx.css.fontSize +import kotlinx.css.height +import kotlinx.css.margin +import kotlinx.css.marginLeft +import kotlinx.css.marginRight +import kotlinx.css.px +import kotlinx.css.width import kotlinx.html.id import kotlinx.html.js.onBlurFunction import kotlinx.html.js.onClickFunction -import react.* -import styled.* +import react.RBuilder +import react.RProps +import react.ReactElement +import react.child +import react.functionalComponent +import styled.css +import styled.styledA +import styled.styledDiv +import styled.styledH1 +import styled.styledImg +import styled.styledNav @Suppress("FunctionName") fun RBuilder.NavBar(handler: NavBarProps.() -> Unit): ReactElement{ diff --git a/web-app/src/main/kotlin/root/RootR.kt b/web-app/src/main/kotlin/root/RootR.kt index d14116e0..260d1ef9 100644 --- a/web-app/src/main/kotlin/root/RootR.kt +++ b/web-app/src/main/kotlin/root/RootR.kt @@ -18,7 +18,7 @@ package root import com.arkivanov.decompose.RouterState import com.shabinder.common.root.SpotiFlyerRoot -import com.shabinder.common.root.SpotiFlyerRoot.* +import com.shabinder.common.root.SpotiFlyerRoot.Child import extras.RenderableRootComponent import extras.renderableChild import home.HomeScreen