mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +01:00
TrackList Ui Fix
This commit is contained in:
parent
50fa91fbca
commit
80e6ecf1f3
@ -19,6 +19,7 @@ import com.shabinder.android.utils.requestStoragePermission
|
|||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.di.createDirectories
|
import com.shabinder.common.di.createDirectories
|
||||||
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.root.SpotiFlyerRootContent
|
import com.shabinder.common.root.SpotiFlyerRootContent
|
||||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||||
@ -26,6 +27,8 @@ import com.shabinder.common.ui.SpotiFlyerTheme
|
|||||||
import com.shabinder.common.ui.colorOffWhite
|
import com.shabinder.common.ui.colorOffWhite
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
const val disableDozeCode = 1223
|
const val disableDozeCode = 1223
|
||||||
@ -38,6 +41,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
private lateinit var root: SpotiFlyerRoot
|
private lateinit var root: SpotiFlyerRoot
|
||||||
private val callBacks: SpotiFlyerRootCallBacks
|
private val callBacks: SpotiFlyerRootCallBacks
|
||||||
get() = root.callBacks
|
get() = root.callBacks
|
||||||
|
//TODO pass updates from Foreground Service
|
||||||
|
private val downloadFlow = MutableStateFlow(hashMapOf<String, DownloadStatus>())
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -66,6 +72,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
override val database = this@MainActivity.database
|
override val database = this@MainActivity.database
|
||||||
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
||||||
override val directories: Dir = this@MainActivity.dir
|
override val directories: Dir = this@MainActivity.dir
|
||||||
|
override val downloadProgressReport: StateFlow<HashMap<String, DownloadStatus>> = downloadFlow
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
package com.shabinder.common.list
|
package com.shabinder.common.list
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.list.integration.SpotiFlyerListImpl
|
import com.shabinder.common.list.integration.SpotiFlyerListImpl
|
||||||
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.spotify.Source
|
import com.shabinder.common.models.spotify.Source
|
||||||
import com.shabinder.common.utils.Consumer
|
import com.shabinder.common.utils.Consumer
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface SpotiFlyerList {
|
interface SpotiFlyerList {
|
||||||
|
|
||||||
@ -20,10 +24,11 @@ interface SpotiFlyerList {
|
|||||||
* Download All Tracks(after filtering already Downloaded)
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
* */
|
* */
|
||||||
fun onDownloadAllClicked(trackList:List<TrackDetails>)
|
fun onDownloadAllClicked(trackList:List<TrackDetails>)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download All Tracks(after filtering already Downloaded)
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
* */
|
* */
|
||||||
fun onDownloadClicked(wholeTrackList:List<TrackDetails>, trackIndex:Int)
|
fun onDownloadClicked(track:TrackDetails)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To Pop and return back to Main Screen
|
* To Pop and return back to Main Screen
|
||||||
@ -41,6 +46,7 @@ interface SpotiFlyerList {
|
|||||||
val dir: Dir
|
val dir: Dir
|
||||||
val link: String
|
val link: String
|
||||||
val listOutput: Consumer<Output>
|
val listOutput: Consumer<Output>
|
||||||
|
val downloadProgressFlow: StateFlow<HashMap<String,DownloadStatus>>
|
||||||
}
|
}
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
object Finished : Output()
|
object Finished : Output()
|
||||||
@ -50,7 +56,8 @@ interface SpotiFlyerList {
|
|||||||
"","",
|
"","",
|
||||||
"Loading","", emptyList(),
|
"Loading","", emptyList(),
|
||||||
Source.Spotify),
|
Source.Spotify),
|
||||||
val link:String = ""
|
val link:String = "",
|
||||||
|
val trackList:SnapshotStateList<TrackDetails> = mutableStateListOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import com.shabinder.common.ui.colorAccent
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ fun SpotiFlyerListContent(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val model by component.models.collectAsState(SpotiFlyerList.State())
|
val model by component.models.collectAsState(SpotiFlyerList.State())
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
@ -44,10 +46,10 @@ fun SpotiFlyerListContent(
|
|||||||
item {
|
item {
|
||||||
CoverImage(result.title, result.coverUrl, coroutineScope,component::loadImage)
|
CoverImage(result.title, result.coverUrl, coroutineScope,component::loadImage)
|
||||||
}
|
}
|
||||||
itemsIndexed(result.trackList) { index, item ->
|
itemsIndexed(model.trackList) { index, item ->
|
||||||
TrackCard(
|
TrackCard(
|
||||||
track = item,
|
track = item,
|
||||||
downloadTrack = { component.onDownloadClicked(result.trackList,index) },
|
downloadTrack = { component.onDownloadClicked(item) },
|
||||||
loadImage = component::loadImage
|
loadImage = component::loadImage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -88,22 +90,22 @@ fun TrackCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
when(track.downloaded){
|
when(track.downloaded){
|
||||||
DownloadStatus.Downloaded -> {
|
is DownloadStatus.Downloaded -> {
|
||||||
DownloadImageTick()
|
DownloadImageTick()
|
||||||
}
|
}
|
||||||
DownloadStatus.Queued -> {
|
is DownloadStatus.Queued -> {
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
DownloadStatus.Failed -> {
|
is DownloadStatus.Failed -> {
|
||||||
DownloadImageError()
|
DownloadImageError()
|
||||||
}
|
}
|
||||||
DownloadStatus.Downloading -> {
|
is DownloadStatus.Downloading -> {
|
||||||
CircularProgressIndicator(progress = track.progress.toFloat()/100f)
|
CircularProgressIndicator(progress = (track.downloaded as DownloadStatus.Downloading).progress.toFloat()/100f)
|
||||||
}
|
}
|
||||||
DownloadStatus.Converting -> {
|
is DownloadStatus.Converting -> {
|
||||||
CircularProgressIndicator(progress = 100f,color = colorAccent)
|
CircularProgressIndicator(progress = 100f,color = colorAccent)
|
||||||
}
|
}
|
||||||
DownloadStatus.NotDownloaded -> {
|
is DownloadStatus.NotDownloaded -> {
|
||||||
DownloadImageArrow(Modifier.clickable(onClick = {
|
DownloadImageArrow(Modifier.clickable(onClick = {
|
||||||
downloadTrack()
|
downloadTrack()
|
||||||
}))
|
}))
|
||||||
|
@ -23,7 +23,8 @@ internal class SpotiFlyerListImpl(
|
|||||||
dir = this.dir,
|
dir = this.dir,
|
||||||
storeFactory = storeFactory,
|
storeFactory = storeFactory,
|
||||||
fetchQuery = fetchQuery,
|
fetchQuery = fetchQuery,
|
||||||
link = link
|
link = link,
|
||||||
|
downloadProgressFlow = downloadProgressFlow
|
||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,8 +34,8 @@ internal class SpotiFlyerListImpl(
|
|||||||
store.accept(Intent.StartDownloadAll(trackList))
|
store.accept(Intent.StartDownloadAll(trackList))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDownloadClicked(wholeTrackList: List<TrackDetails>, trackIndex: Int) {
|
override fun onDownloadClicked(track:TrackDetails) {
|
||||||
store.accept(Intent.StartDownload(wholeTrackList,trackIndex))
|
store.accept(Intent.StartDownload(track))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed(){
|
override fun onBackPressed(){
|
||||||
|
@ -7,8 +7,8 @@ import com.shabinder.common.list.store.SpotiFlyerListStore.*
|
|||||||
|
|
||||||
internal interface SpotiFlyerListStore: Store<Intent, State, Nothing> {
|
internal interface SpotiFlyerListStore: Store<Intent, State, Nothing> {
|
||||||
sealed class Intent {
|
sealed class Intent {
|
||||||
data class StartDownloadAll(val trackList: List<TrackDetails>): Intent()
|
|
||||||
data class StartDownload(val wholeTrackList: List<TrackDetails>, val trackIndex:Int): Intent()
|
|
||||||
data class SearchLink(val link: String): Intent()
|
data class SearchLink(val link: String): Intent()
|
||||||
|
data class StartDownload(val track:TrackDetails): Intent()
|
||||||
|
data class StartDownloadAll(val trackList: List<TrackDetails>): Intent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.shabinder.common.list.store
|
package com.shabinder.common.list.store
|
||||||
|
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import com.arkivanov.mvikotlin.core.store.*
|
import com.arkivanov.mvikotlin.core.store.*
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
@ -11,12 +13,15 @@ import com.shabinder.common.models.DownloadStatus
|
|||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.ui.showPopUpMessage
|
import com.shabinder.common.ui.showPopUpMessage
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
internal class SpotiFlyerListStoreProvider(
|
internal class SpotiFlyerListStoreProvider(
|
||||||
private val dir: Dir,
|
private val dir: Dir,
|
||||||
private val storeFactory: StoreFactory,
|
private val storeFactory: StoreFactory,
|
||||||
private val fetchQuery: FetchPlatformQueryResult,
|
private val fetchQuery: FetchPlatformQueryResult,
|
||||||
private val link: String
|
private val link: String,
|
||||||
|
private val downloadProgressFlow: StateFlow<HashMap<String, DownloadStatus>>
|
||||||
) {
|
) {
|
||||||
fun provide(): SpotiFlyerListStore =
|
fun provide(): SpotiFlyerListStore =
|
||||||
object : SpotiFlyerListStore, Store<Intent, State, Nothing> by storeFactory.create(
|
object : SpotiFlyerListStore, Store<Intent, State, Nothing> by storeFactory.create(
|
||||||
@ -28,23 +33,29 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
private sealed class Result {
|
private sealed class Result {
|
||||||
data class ResultFetched(val result: PlatformQueryResult) : Result()
|
data class ResultFetched(val result: PlatformQueryResult,val trackList: SnapshotStateList<TrackDetails>) : Result()
|
||||||
data class SearchLink(val link: String) : Result()
|
data class UpdateTrackList(val list:SnapshotStateList<TrackDetails>): Result()
|
||||||
data class UpdateTrackList(val list:List<TrackDetails>): Result()
|
data class UpdateTrackItem(val item:TrackDetails): Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||||
|
|
||||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||||
fetchQuery.query(link)?.let{
|
executeIntent(Intent.SearchLink(link),getState)
|
||||||
dispatch(Result.ResultFetched(it))
|
|
||||||
|
downloadProgressFlow.collectLatest { map ->
|
||||||
|
val updatedTrackList = getState().trackList.updateTracksStatuses(map)
|
||||||
|
if(updatedTrackList.isNotEmpty()) dispatch(Result.UpdateTrackList(updatedTrackList))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
when (intent) {//TODO: Add Dispatchers where needed
|
when (intent) {
|
||||||
is Intent.SearchLink -> fetchQuery.query(link)?.let{
|
is Intent.SearchLink -> fetchQuery.query(link)?.let{ result ->
|
||||||
dispatch((Result.ResultFetched(it)))
|
result.trackList = result.trackList.toMutableList()
|
||||||
|
dispatch((Result.ResultFetched(result,result.trackList.toMutableList().updateTracksStatuses(downloadProgressFlow.value))))
|
||||||
}
|
}
|
||||||
|
|
||||||
is Intent.StartDownloadAll -> {
|
is Intent.StartDownloadAll -> {
|
||||||
val finalList =
|
val finalList =
|
||||||
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
||||||
@ -57,17 +68,11 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
}
|
}
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
dispatch(Result.UpdateTrackList(list))
|
dispatch(Result.UpdateTrackList(list.toMutableList().updateTracksStatuses(downloadProgressFlow.value)))
|
||||||
}
|
|
||||||
is Intent.StartDownload -> {
|
|
||||||
val trackList = intent.wholeTrackList.toMutableList()
|
|
||||||
val track = trackList.getOrNull(intent.trackIndex)
|
|
||||||
?.apply { downloaded = DownloadStatus.Queued }
|
|
||||||
track?.let {
|
|
||||||
trackList[intent.trackIndex] = it
|
|
||||||
dispatch(Result.UpdateTrackList(trackList))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is Intent.StartDownload -> {
|
||||||
|
dispatch(Result.UpdateTrackItem(intent.track.apply { downloaded = DownloadStatus.Queued }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,9 +81,31 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
private object ReducerImpl : Reducer<State, Result> {
|
private object ReducerImpl : Reducer<State, Result> {
|
||||||
override fun State.reduce(result: Result): State =
|
override fun State.reduce(result: Result): State =
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.ResultFetched -> copy(queryResult = result.result)
|
is Result.ResultFetched -> copy(queryResult = result.result, trackList = result.trackList ,link = link)
|
||||||
is Result.SearchLink -> copy(link = result.link)
|
is Result.UpdateTrackList -> copy(trackList = result.list)
|
||||||
is Result.UpdateTrackList -> copy(queryResult = this.queryResult?.apply { trackList = result.list })
|
is Result.UpdateTrackItem -> updateTrackItem(result.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun State.updateTrackItem(item: TrackDetails):State{
|
||||||
|
val position = this.trackList.map { it.title }.indexOf(item.title)
|
||||||
|
if(position != -1){
|
||||||
|
return copy(trackList = trackList.apply { set(position,item) })
|
||||||
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MutableList<TrackDetails>.updateTracksStatuses(map:HashMap<String,DownloadStatus>):SnapshotStateList<TrackDetails>{
|
||||||
|
val titleList = this.map { it.title }
|
||||||
|
val newStateList = mutableStateListOf<TrackDetails>()
|
||||||
|
for(newTrack in map){
|
||||||
|
titleList.indexOf(newTrack.key).let { position ->
|
||||||
|
this.getOrNull(position)?.apply { downloaded = newTrack.value }?.also { updatedTrack ->
|
||||||
|
this[position] = updatedTrack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newStateList.addAll(this)
|
||||||
|
return newStateList
|
||||||
|
}
|
@ -8,11 +8,13 @@ import com.shabinder.common.di.Dir
|
|||||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import com.shabinder.common.root.integration.SpotiFlyerRootImpl
|
import com.shabinder.common.root.integration.SpotiFlyerRootImpl
|
||||||
import com.shabinder.common.utils.Consumer
|
import com.shabinder.common.utils.Consumer
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface SpotiFlyerRoot {
|
interface SpotiFlyerRoot {
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ interface SpotiFlyerRoot {
|
|||||||
val database: Database
|
val database: Database
|
||||||
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||||
val directories: Dir
|
val directories: Dir
|
||||||
|
val downloadProgressReport: StateFlow<HashMap<String,DownloadStatus>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,13 @@ import com.arkivanov.decompose.value.Value
|
|||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||||
import com.shabinder.common.utils.Consumer
|
import com.shabinder.common.utils.Consumer
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
internal class SpotiFlyerRootImpl(
|
internal class SpotiFlyerRootImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
@ -58,6 +60,7 @@ internal class SpotiFlyerRootImpl(
|
|||||||
override val dir: Dir = directories
|
override val dir: Dir = directories
|
||||||
override val link: String = link
|
override val link: String = link
|
||||||
override val listOutput : Consumer<SpotiFlyerList.Output> = Consumer(::onListOutput)
|
override val listOutput : Consumer<SpotiFlyerList.Output> = Consumer(::onListOutput)
|
||||||
|
override val downloadProgressFlow = downloadProgressReport
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -72,16 +72,15 @@ actual class Dir actual constructor(
|
|||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
actual suspend fun saveFileWithMetadata(
|
actual suspend fun saveFileWithMetadata(
|
||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
path: String,
|
|
||||||
trackDetails: TrackDetails
|
trackDetails: TrackDetails
|
||||||
) {
|
) {
|
||||||
val file = File(path)
|
val file = File(trackDetails.outputFilePath)
|
||||||
file.writeBytes(mp3ByteArray)
|
file.writeBytes(mp3ByteArray)
|
||||||
|
|
||||||
Mp3File(file)
|
Mp3File(file)
|
||||||
.removeAllTags()
|
.removeAllTags()
|
||||||
.setId3v1Tags(trackDetails)
|
.setId3v1Tags(trackDetails)
|
||||||
.setId3v2TagsAndSaveFile(trackDetails,path)
|
.setId3v2TagsAndSaveFile(trackDetails)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual suspend fun loadImage(url: String): ImageBitmap? {
|
actual suspend fun loadImage(url: String): ImageBitmap? {
|
@ -1,5 +1,6 @@
|
|||||||
package com.shabinder.common.di.providers
|
package com.shabinder.common.di.providers
|
||||||
|
|
||||||
|
import co.touchlab.kermit.Kermit
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.YoutubeTrack
|
import com.shabinder.common.models.YoutubeTrack
|
||||||
@ -13,7 +14,7 @@ import kotlin.math.absoluteValue
|
|||||||
private const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
private const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
||||||
|
|
||||||
class YoutubeMusic constructor(
|
class YoutubeMusic constructor(
|
||||||
private val logger: Logger,
|
private val logger: Kermit,
|
||||||
private val httpClient:HttpClient,
|
private val httpClient:HttpClient,
|
||||||
) {
|
) {
|
||||||
private val tag = "YT Music"
|
private val tag = "YT Music"
|
||||||
@ -166,7 +167,7 @@ class YoutubeMusic constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.i(youtubeTracks.joinToString(" abc \n"),tag)
|
//logger.d(youtubeTracks.joinToString(" abc \n"),tag)
|
||||||
return youtubeTracks
|
return youtubeTracks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,7 @@ import com.github.kiulian.downloader.model.quality.AudioQuality
|
|||||||
import com.shabinder.common.models.DownloadResult
|
import com.shabinder.common.models.DownloadResult
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
actual fun openPlatform(packageID:String, platformLink:String){
|
actual fun openPlatform(packageID:String, platformLink:String){
|
||||||
@ -24,7 +22,7 @@ actual fun giveDonation(){
|
|||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
val DownloadProgressFlow = MutableStateFlow(Pair<String,DownloadStatus>("",DownloadStatus.Queued))
|
val DownloadProgressFlow: MutableStateFlow<HashMap<String,DownloadStatus>> = MutableStateFlow(hashMapOf())
|
||||||
|
|
||||||
actual suspend fun downloadTracks(
|
actual suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
@ -38,7 +36,7 @@ actual suspend fun downloadTracks(
|
|||||||
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
||||||
val videoId = getYTIDBestMatch(searchQuery,it)
|
val videoId = getYTIDBestMatch(searchQuery,it)
|
||||||
if (videoId.isNullOrBlank()) {
|
if (videoId.isNullOrBlank()) {
|
||||||
DownloadProgressFlow.emit(Pair(it.title,DownloadStatus.Failed))
|
DownloadProgressFlow.emit(DownloadProgressFlow.value.apply { set(it.title,DownloadStatus.Failed) })
|
||||||
} else {//Found Youtube Video ID
|
} else {//Found Youtube Video ID
|
||||||
downloadTrack(videoId, it,saveFileWithMetaData)
|
downloadTrack(videoId, it,saveFileWithMetaData)
|
||||||
}
|
}
|
||||||
@ -46,7 +44,7 @@ actual suspend fun downloadTracks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val ytDownloader = YoutubeDownloader()
|
private val ytDownloader = YoutubeDownloader()
|
||||||
|
|
||||||
suspend fun downloadTrack(
|
suspend fun downloadTrack(
|
||||||
videoID: String,
|
videoID: String,
|
||||||
@ -61,14 +59,14 @@ suspend fun downloadTrack(
|
|||||||
downloadFile(url).collect {
|
downloadFile(url).collect {
|
||||||
when(it){
|
when(it){
|
||||||
is DownloadResult.Error -> {
|
is DownloadResult.Error -> {
|
||||||
//TODO()
|
DownloadProgressFlow.emit(DownloadProgressFlow.value.apply { set(trackDetails.title,DownloadStatus.Failed) })
|
||||||
}
|
}
|
||||||
is DownloadResult.Progress -> {
|
is DownloadResult.Progress -> {
|
||||||
DownloadProgressFlow.emit(Pair(trackDetails.title,DownloadStatus.Downloading(it.progress)))
|
DownloadProgressFlow.emit(DownloadProgressFlow.value.apply { set(trackDetails.title,DownloadStatus.Downloading(it.progress)) })
|
||||||
}
|
}
|
||||||
is DownloadResult.Success -> {
|
is DownloadResult.Success -> {//Todo clear map
|
||||||
saveFileWithMetaData(it.byteArray,trackDetails)
|
saveFileWithMetaData(it.byteArray,trackDetails)
|
||||||
DownloadProgressFlow.emit(Pair(trackDetails.title,DownloadStatus.Downloaded))
|
DownloadProgressFlow.emit(DownloadProgressFlow.value.apply { set(trackDetails.title,DownloadStatus.Downloaded) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,10 @@ import javax.imageio.ImageIO
|
|||||||
|
|
||||||
actual class Dir actual constructor(private val logger: Kermit) {
|
actual class Dir actual constructor(private val logger: Kermit) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
createDirectories()
|
||||||
|
}
|
||||||
|
|
||||||
actual fun fileSeparator(): String = File.separator
|
actual fun fileSeparator(): String = File.separator
|
||||||
|
|
||||||
actual fun imageCacheDir(): String = System.getProperty("user.home") +
|
actual fun imageCacheDir(): String = System.getProperty("user.home") +
|
||||||
|
@ -2,6 +2,7 @@ import androidx.compose.desktop.DesktopMaterialTheme
|
|||||||
import androidx.compose.desktop.Window
|
import androidx.compose.desktop.Window
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
@ -10,6 +11,7 @@ import com.arkivanov.mvikotlin.core.lifecycle.LifecycleRegistry
|
|||||||
import com.arkivanov.mvikotlin.core.lifecycle.resume
|
import com.arkivanov.mvikotlin.core.lifecycle.resume
|
||||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
|
import com.shabinder.common.di.DownloadProgressFlow
|
||||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.di.initKoin
|
import com.shabinder.common.di.initKoin
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
@ -19,6 +21,9 @@ import com.shabinder.common.ui.SpotiFlyerShapes
|
|||||||
import com.shabinder.common.ui.SpotiFlyerTypography
|
import com.shabinder.common.ui.SpotiFlyerTypography
|
||||||
import com.shabinder.common.ui.colorOffWhite
|
import com.shabinder.common.ui.colorOffWhite
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||||
|
|
||||||
@ -38,7 +43,13 @@ fun main(){
|
|||||||
typography = SpotiFlyerTypography,
|
typography = SpotiFlyerTypography,
|
||||||
shapes = SpotiFlyerShapes
|
shapes = SpotiFlyerShapes
|
||||||
) {
|
) {
|
||||||
SpotiFlyerRootContent(rootComponent(factory = ::spotiFlyerRoot))
|
val callBacks = SpotiFlyerRootContent(rootComponent(factory = ::spotiFlyerRoot)).callBacks
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
scope.launch {
|
||||||
|
DownloadProgressFlow.collect {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,5 +63,6 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
override val database: Database = koin.get()
|
override val database: Database = koin.get()
|
||||||
override val fetchPlatformQueryResult: FetchPlatformQueryResult = koin.get()
|
override val fetchPlatformQueryResult: FetchPlatformQueryResult = koin.get()
|
||||||
override val directories: Dir = koin.get()
|
override val directories: Dir = koin.get()
|
||||||
|
override val downloadProgressReport = DownloadProgressFlow
|
||||||
}
|
}
|
||||||
)
|
)
|
Loading…
Reference in New Issue
Block a user