mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 01:04:31 +01:00
Code Cleaned & Ktlint Added
This commit is contained in:
parent
46e5e89a2e
commit
ccea676b77
@ -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.*
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
id("org.jlleitschuh.gradle.ktlint")
|
||||
id("org.jlleitschuh.gradle.ktlint-idea")
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -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)
|
||||
|
@ -21,9 +21,12 @@ 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"
|
||||
|
||||
// Code Formatting
|
||||
const val ktLint = "10.0.0"
|
||||
|
||||
// DI
|
||||
const val koin = "3.0.1-beta-1"
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("ktlint-setup")
|
||||
}
|
||||
|
||||
android {
|
||||
@ -43,5 +44,4 @@ android {
|
||||
res.srcDirs("src/androidMain/res")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,9 +14,31 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
sealed class NetworkResponse<out T> {
|
||||
data class Success<T>(val value:T):NetworkResponse<T>()
|
||||
data class Error(val message:String):NetworkResponse<Nothing>()
|
||||
plugins {
|
||||
id("org.jlleitschuh.gradle.ktlint")
|
||||
id("org.jlleitschuh.gradle.ktlint-idea")
|
||||
}
|
||||
|
||||
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<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
|
||||
debug.set(true)
|
||||
}*/
|
||||
}
|
@ -18,6 +18,7 @@ plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-multiplatform")
|
||||
id("org.jetbrains.compose")
|
||||
id("ktlint-setup")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
@ -17,6 +17,7 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-multiplatform")
|
||||
id("ktlint-setup")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -47,9 +48,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
named("jsTest") {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
dependencies {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,17 +14,11 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 {
|
||||
@ -37,9 +31,7 @@ kotlin {
|
||||
}
|
||||
sourceSets {
|
||||
named("commonMain") {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
dependencies {}
|
||||
}
|
||||
|
||||
named("androidMain") {
|
||||
|
@ -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
|
||||
@ -68,7 +74,6 @@ actual fun pristineFont() = FontFamily(
|
||||
Font(R.font.pristine_script, FontWeight.Bold)
|
||||
)
|
||||
|
||||
|
||||
@Composable
|
||||
actual fun DownloadImageTick() {
|
||||
Image(
|
||||
|
@ -17,7 +17,7 @@
|
||||
@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
|
||||
|
@ -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,8 +57,6 @@ fun SpotiFlyerListContent(
|
||||
) {
|
||||
val model by component.models.collectAsState(SpotiFlyerList.State())
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
// TODO Better Null Handling
|
||||
val result = model.queryResult
|
||||
@ -60,7 +71,7 @@ fun SpotiFlyerListContent(
|
||||
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(
|
||||
@ -124,9 +135,13 @@ fun TrackCard(
|
||||
CircularProgressIndicator(progress = 100f, color = colorAccent)
|
||||
}
|
||||
is DownloadStatus.NotDownloaded -> {
|
||||
DownloadImageArrow(Modifier.clickable(onClick = {
|
||||
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 {
|
||||
|
@ -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
|
||||
@ -146,10 +174,15 @@ fun SearchPanel(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
|
||||
modifier = modifier.padding(12.dp).fillMaxWidth()
|
||||
.border(
|
||||
BorderStroke(2.dp, Brush.horizontalGradient(listOf(
|
||||
BorderStroke(
|
||||
2.dp,
|
||||
Brush.horizontalGradient(
|
||||
listOf(
|
||||
colorPrimary,
|
||||
colorAccent
|
||||
))),
|
||||
)
|
||||
)
|
||||
),
|
||||
RoundedCornerShape(30.dp)
|
||||
),
|
||||
shape = RoundedCornerShape(size = 30.dp),
|
||||
@ -168,10 +201,15 @@ fun SearchPanel(
|
||||
onSearch(link)
|
||||
}
|
||||
},
|
||||
border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(
|
||||
border = BorderStroke(
|
||||
1.dp,
|
||||
Brush.horizontalGradient(
|
||||
listOf(
|
||||
colorPrimary,
|
||||
colorAccent
|
||||
)))
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
Text(text = "Search", style = SpotiFlyerTypography.h6, modifier = Modifier.padding(4.dp))
|
||||
}
|
||||
@ -199,28 +237,35 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"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/") }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -237,9 +282,11 @@ 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))
|
||||
@ -293,9 +340,11 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(onClick = {
|
||||
.clickable(
|
||||
onClick = {
|
||||
shareApp()
|
||||
}),
|
||||
}
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.Share, "Share SpotiFlyer App")
|
||||
@ -325,7 +374,8 @@ fun HistoryColumn(
|
||||
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),
|
||||
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)
|
||||
@ -375,15 +425,16 @@ fun DownloadRecordItem(
|
||||
Image(
|
||||
imageVector = ShareImage(),
|
||||
"Research",
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
modifier = Modifier.clickable(
|
||||
onClick = {
|
||||
// if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else
|
||||
onItemClicked(item.link)
|
||||
})
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun HomeCategoryTabIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
|
@ -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
|
||||
|
@ -20,7 +20,7 @@ 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,
|
||||
|
@ -23,7 +23,6 @@ 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -97,7 +102,6 @@ actual fun ShareImage():ImageVector = vectorXmlResource("drawable/ic_share_open.
|
||||
@Composable
|
||||
actual fun PlaceHolderImage(): ImageVector = vectorXmlResource("drawable/music.xml")
|
||||
|
||||
|
||||
@Composable
|
||||
actual fun SpotiFlyerLogo(): ImageVector =
|
||||
vectorXmlResource("drawable/ic_spotiflyer_logo.xml")
|
||||
|
@ -41,7 +41,6 @@ data class TrackDetails(
|
||||
var videoID: String? = null,
|
||||
) : Parcelable
|
||||
|
||||
|
||||
@Serializable
|
||||
sealed class DownloadStatus : Parcelable {
|
||||
@Parcelize object Downloaded : DownloadStatus()
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.shabinder.common.models.gaana
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
@ -37,4 +37,5 @@ data class Album(
|
||||
var release_date_precision: String? = null,
|
||||
var tracks: PagingObjectTrack? = null,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -25,4 +25,5 @@ data class Artist(
|
||||
var id: String? = null,
|
||||
var name: String? = null,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -21,4 +21,5 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class Copyright(
|
||||
var text: String? = null,
|
||||
var type: String? = null)
|
||||
var type: String? = null
|
||||
)
|
||||
|
@ -21,4 +21,5 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class Followers(
|
||||
var href: String? = null,
|
||||
var total: Int? = null)
|
||||
var total: Int? = null
|
||||
)
|
||||
|
@ -22,4 +22,5 @@ import kotlinx.serialization.Serializable
|
||||
data class Image(
|
||||
var width: Int? = null,
|
||||
var height: Int? = null,
|
||||
var url: String? = null)
|
||||
var url: String? = null
|
||||
)
|
||||
|
@ -24,4 +24,5 @@ data class LinkedTrack(
|
||||
var href: String? = null,
|
||||
var id: String? = null,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -26,4 +26,5 @@ data class PagingObjectPlaylistTrack(
|
||||
var next: String? = null,
|
||||
var offset: Int = 0,
|
||||
var previous: String? = null,
|
||||
var total: Int = 0)
|
||||
var total: Int = 0
|
||||
)
|
||||
|
@ -26,4 +26,5 @@ data class PagingObjectTrack(
|
||||
var next: String? = null,
|
||||
var offset: Int = 0,
|
||||
var previous: String? = null,
|
||||
var total: Int = 0)
|
||||
var total: Int = 0
|
||||
)
|
||||
|
@ -34,4 +34,5 @@ data class Playlist(
|
||||
var snapshot_id: String? = null,
|
||||
var tracks: PagingObjectPlaylistTrack? = null,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -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)
|
||||
var is_local: Boolean? = null
|
||||
)
|
||||
|
@ -40,4 +40,3 @@ data class Track(
|
||||
var popularity: Int? = null,
|
||||
var downloaded: DownloadStatus = DownloadStatus.NotDownloaded
|
||||
)
|
||||
|
||||
|
@ -30,4 +30,5 @@ data class UserPrivate(
|
||||
var images: List<Image?>? = null,
|
||||
var product: String,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -27,4 +27,5 @@ data class UserPublic(
|
||||
var id: String? = null,
|
||||
var images: List<Image?>? = null,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
||||
var uri: String? = null
|
||||
)
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package com.shabinder.common.models.wynk
|
||||
|
||||
|
||||
|
||||
// Use Kotlinx JSON Parsing as in YT Music
|
||||
data class ShortURLWynk(
|
||||
val actualTotal: Int,
|
||||
|
@ -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
|
||||
@ -63,16 +62,11 @@ actual class Dir actual constructor(
|
||||
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
|
||||
{
|
||||
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" }
|
||||
}
|
||||
}
|
||||
@ -151,7 +145,8 @@ actual class Dir actual constructor(
|
||||
logger.d { "Scanning File" }
|
||||
MediaScannerConnection.scanFile(
|
||||
appContext,
|
||||
listOf(path).toTypedArray(), null,null)
|
||||
listOf(path).toTypedArray(), null, null
|
||||
)
|
||||
}
|
||||
|
||||
actual suspend fun loadImage(url: String): Picture {
|
||||
|
@ -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
|
||||
@ -83,17 +82,17 @@ class ConnectionLiveData(context: Context = appContext) : LiveData<Boolean>() {
|
||||
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}")
|
||||
Log.d(TAG, "onAvailable: adding network. $network")
|
||||
validNetworks.add(network)
|
||||
checkValidNetworks()
|
||||
}
|
||||
@ -107,11 +106,10 @@ class ConnectionLiveData(context: Context = appContext) : LiveData<Boolean>() {
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,18 +40,28 @@ 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 {
|
||||
@ -78,7 +95,6 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
private val ytDownloader: YoutubeDownloader
|
||||
get() = fetcher.youtubeProvider.ytDownloader
|
||||
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? = null
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
@ -117,9 +133,11 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
}
|
||||
}
|
||||
|
||||
val downloadObjects: ArrayList<TrackDetails>? = (it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList(
|
||||
val downloadObjects: ArrayList<TrackDetails>? = (
|
||||
it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList(
|
||||
"object"
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
downloadObjects?.let { list ->
|
||||
downloadObjects.size.let { size ->
|
||||
@ -175,7 +193,6 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun downloadTrack(videoID: String, track: TrackDetails) {
|
||||
launch {
|
||||
try {
|
||||
@ -193,19 +210,19 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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" }
|
||||
},
|
||||
{ error ->
|
||||
logger.d(tag){"Enqueuing Error:${error.throwable.toString()}"}
|
||||
logger.d(tag) { "Enqueuing Error:${error.throwable}" }
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -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"
|
||||
@ -375,8 +391,6 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is the method that can be called to update the Notification
|
||||
*/
|
||||
@ -467,7 +481,7 @@ class ForegroundService : Service(),CoroutineScope{
|
||||
}
|
||||
|
||||
private fun getNotification(): Notification = NotificationCompat.Builder(this, channelId).run {
|
||||
setSmallIcon(drawable.ic_download_arrow)
|
||||
setSmallIcon(R.drawable.ic_download_arrow)
|
||||
setContentTitle("Total: $total Completed:$converted Failed:$failed")
|
||||
setSilent(true)
|
||||
setStyle(
|
||||
@ -479,7 +493,7 @@ 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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -51,10 +54,12 @@ fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get()) }
|
||||
}
|
||||
|
||||
val kotlinxSerializer = KotlinxSerializer( Json {
|
||||
val kotlinxSerializer = KotlinxSerializer(
|
||||
Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
fun createHttpClient(enableNetworkLogs: Boolean = false, serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
|
||||
install(JsonFeature) {
|
||||
|
@ -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
|
||||
|
@ -22,13 +22,12 @@ 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,
|
||||
|
@ -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,
|
||||
|
@ -19,9 +19,13 @@ 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) {
|
||||
corsProxy.url
|
||||
|
@ -25,7 +25,7 @@ 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,
|
||||
@ -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,17 +94,7 @@ 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
|
||||
@ -134,7 +106,6 @@ class GaanaProvider(
|
||||
folderType = "Artist"
|
||||
subFolder = link
|
||||
coverUrl = gaanaPlaceholderImageUrl
|
||||
val artistDetails =
|
||||
getGaanaArtistDetails(seokey = link).artist.firstOrNull()
|
||||
?.also {
|
||||
title = it.name
|
||||
@ -142,22 +113,13 @@ class GaanaProvider(
|
||||
}
|
||||
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
|
||||
@ -180,4 +142,17 @@ class GaanaProvider(
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -55,8 +59,7 @@ class SpotifyProvider(
|
||||
return if (token == null) {
|
||||
logger.d { "Please Check your Network Connection" }
|
||||
null
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
logger.d { "Spotify Provider Created with $token" }
|
||||
httpClient = HttpClient {
|
||||
defaultRequest {
|
||||
@ -89,7 +92,6 @@ class SpotifyProvider(
|
||||
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
|
||||
val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
|
||||
|
||||
|
||||
if (type == "Error" || link == "Error") {
|
||||
return null
|
||||
}
|
||||
@ -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(
|
||||
@ -172,8 +156,10 @@ class SpotifyProvider(
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,17 +172,7 @@ class SpotifyProvider(
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@ -257,4 +233,17 @@ class SpotifyProvider(
|
||||
outputFilePath = dir.finalOutputDir(it.name.toString(), type, subFolder, dir.defaultDir()/*,".m4a"*/)
|
||||
)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -21,10 +21,7 @@ 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,
|
||||
|
@ -21,10 +21,21 @@ 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"
|
||||
@ -65,8 +76,10 @@ class YoutubeMusic constructor(
|
||||
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
|
||||
@ -219,7 +232,6 @@ class YoutubeMusic constructor(
|
||||
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
|
||||
@ -259,7 +271,9 @@ class YoutubeMusic constructor(
|
||||
linksWithMatchValue[result.videoId.toString()] = avgMatch.toInt()
|
||||
}
|
||||
// logger.d("YT Api Result"){"$trackName - $linksWithMatchValue"}
|
||||
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap()
|
||||
return linksWithMatchValue.toList().sortedByDescending { it.second }.toMap().also {
|
||||
logger.d(tag) { "Match Found for $trackName - ${!it.isNullOrEmpty()}" }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getYoutubeMusicResponse(query: String): String {
|
||||
|
@ -19,13 +19,13 @@ 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") {
|
||||
|
@ -21,8 +21,8 @@ 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"
|
||||
|
||||
|
@ -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<Unit>(Channel.UNLIMITED)
|
||||
private val operationQueue = Channel<Operation<*>>(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<Operation<*>?> {
|
||||
@ -67,7 +71,6 @@ class ParallelExecutor(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun <Result> 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<Result>(
|
||||
private val block: suspend () -> Result,
|
||||
) {
|
||||
@ -114,12 +114,10 @@ class ParallelExecutor(
|
||||
|
||||
val result: Deferred<Result> get() = _result
|
||||
|
||||
|
||||
suspend fun execute() {
|
||||
try {
|
||||
_result.complete(block())
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
} catch (e: Throwable) {
|
||||
_result.completeExceptionally(e)
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package com.shabinder.common.di.utils
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removing Illegal Chars from File Name
|
||||
* **/
|
||||
|
@ -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
|
||||
|
||||
@ -44,20 +44,24 @@ interface Yt1sMp3 {
|
||||
* */
|
||||
private suspend fun getKey(videoID: String): String {
|
||||
val response: JsonObject? = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index") {
|
||||
body = FormDataContent(Parameters.build {
|
||||
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 {
|
||||
body = FormDataContent(
|
||||
Parameters.build {
|
||||
append("vid", videoID)
|
||||
append("k", key)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -91,8 +91,11 @@ actual suspend fun downloadTracks(
|
||||
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
||||
val videoId = fetcher.youtubeMusic.getYTIDBestMatch(searchQuery, it)
|
||||
if (videoId.isNullOrBlank()) {
|
||||
DownloadProgressFlow.emit(DownloadProgressFlow.replayCache.getOrElse(0
|
||||
) { hashMapOf() }.apply { set(it.title,DownloadStatus.Failed) })
|
||||
DownloadProgressFlow.emit(
|
||||
DownloadProgressFlow.replayCache.getOrElse(
|
||||
0
|
||||
) { hashMapOf() }.apply { set(it.title, DownloadStatus.Failed) }
|
||||
)
|
||||
} else { // Found Youtube Video ID
|
||||
downloadTrack(videoId, it, dir::saveFileWithMetadata)
|
||||
}
|
||||
@ -116,17 +119,26 @@ suspend fun downloadTrack(
|
||||
downloadFile(url).collect {
|
||||
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) })
|
||||
DownloadProgressFlow.emit(
|
||||
DownloadProgressFlow.replayCache.getOrElse(
|
||||
0
|
||||
) { hashMapOf() }.apply { set(trackDetails.title, DownloadStatus.Downloaded) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,9 +36,6 @@ 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?,
|
||||
@ -60,16 +58,11 @@ actual class Dir actual constructor(
|
||||
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
|
||||
{
|
||||
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" }
|
||||
}
|
||||
}
|
||||
@ -119,8 +112,10 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
private suspend fun freshImage(url: String): ImageBitmap? {
|
||||
return try {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val source = URL(url)
|
||||
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
||||
connection.connectTimeout = 5000
|
||||
@ -140,6 +135,7 @@ actual class Dir actual constructor(
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actual val db: Database?
|
||||
get() = database
|
||||
|
@ -32,7 +32,6 @@ fun Mp3File.removeAllTags(): Mp3File {
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifying Mp3 with MetaData!
|
||||
**/
|
||||
|
@ -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
|
||||
|
||||
actual class YoutubeProvider actual constructor(
|
||||
private val httpClient: HttpClient,
|
||||
|
@ -20,10 +20,12 @@ 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
|
||||
|
||||
@ -78,7 +80,7 @@ actual suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
) {
|
||||
list.forEach {
|
||||
withContext(Dispatchers.Default) {
|
||||
withContext(dispatcherIO) {
|
||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -24,9 +24,9 @@ fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T
|
||||
getOrCreate(key) { StoreHolder(factory()) }
|
||||
.store
|
||||
|
||||
inline fun <reified T
|
||||
inline fun <reified T :
|
||||
|
||||
: Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
getStore(T::class, factory)
|
||||
|
||||
private class StoreHolder<T : Store<*, *, *>>(
|
||||
|
@ -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,7 +44,9 @@ internal class SpotiFlyerListStoreProvider(
|
||||
) {
|
||||
val logger = getLogger()
|
||||
fun provide(): SpotiFlyerListStore =
|
||||
object : SpotiFlyerListStore, Store<Intent, State, Nothing> by storeFactory.create(
|
||||
object :
|
||||
SpotiFlyerListStore,
|
||||
Store<Intent, State, Nothing> by storeFactory.create(
|
||||
name = "SpotiFlyerListStore",
|
||||
initialState = State(),
|
||||
bootstrapper = SimpleBootstrapper(Unit),
|
||||
|
@ -14,8 +14,6 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import org.jetbrains.compose.compose
|
||||
|
||||
plugins {
|
||||
id("multiplatform-setup")
|
||||
id("android-setup")
|
||||
|
@ -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
|
||||
|
@ -24,9 +24,9 @@ fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T
|
||||
getOrCreate(key) { StoreHolder(factory()) }
|
||||
.store
|
||||
|
||||
inline fun <reified T
|
||||
inline fun <reified T :
|
||||
|
||||
: Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
getStore(T::class, factory)
|
||||
|
||||
private class StoreHolder<T : Store<*, *, *>>(
|
||||
|
@ -43,7 +43,9 @@ internal class SpotiFlyerMainStoreProvider(
|
||||
) {
|
||||
|
||||
fun provide(): SpotiFlyerMainStore =
|
||||
object : SpotiFlyerMainStore, Store<Intent, State, Nothing> by storeFactory.create(
|
||||
object :
|
||||
SpotiFlyerMainStore,
|
||||
Store<Intent, State, Nothing> by storeFactory.create(
|
||||
name = "SpotiFlyerHomeStore",
|
||||
initialState = State(),
|
||||
bootstrapper = SimpleBootstrapper(Unit),
|
||||
@ -64,7 +66,6 @@ internal class SpotiFlyerMainStoreProvider(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private sealed class Result {
|
||||
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
||||
data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result()
|
||||
|
@ -14,8 +14,6 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import org.jetbrains.compose.compose
|
||||
|
||||
plugins {
|
||||
id("multiplatform-setup")
|
||||
id("android-setup")
|
||||
|
@ -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
|
||||
|
@ -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,13 +30,16 @@ 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() {
|
||||
|
||||
val lifecycle = LifecycleRegistry()
|
||||
@ -54,7 +56,7 @@ fun main(){
|
||||
typography = SpotiFlyerTypography,
|
||||
shapes = SpotiFlyerShapes
|
||||
) {
|
||||
val callBacks = SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot)).callBacks
|
||||
SpotiFlyerRootContent(rememberRootComponent(factory = ::spotiFlyerRoot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.willowtreeapps.fuzzywuzzy
|
||||
|
||||
|
||||
/**
|
||||
* Transforms an item of type T to a String.
|
||||
*
|
||||
|
@ -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<EditOp?>
|
||||
|
||||
var dir = 0
|
||||
|
||||
ops = arrayOfNulls(pos)
|
||||
val ops: Array<EditOp?> = 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) {
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ import com.willowtreeapps.fuzzywuzzy.diffutils.ratio.SimpleRatio
|
||||
/**
|
||||
* FuzzySearch facade class
|
||||
*/
|
||||
@Suppress("unused")
|
||||
object FuzzySearch {
|
||||
|
||||
/**
|
||||
|
@ -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<String>? = null
|
||||
|
@ -27,7 +27,6 @@ import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.round
|
||||
|
||||
|
||||
class WeightedRatio : BasicAlgorithm() {
|
||||
|
||||
|
||||
|
@ -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<AppProps, RState>(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{
|
||||
|
@ -14,10 +14,28 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<String,String>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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){
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user