mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-26 10:24:33 +01:00
Added Providers.
This commit is contained in:
parent
dcfca42c40
commit
86431dbc1c
@ -1,6 +1,13 @@
|
||||
package com.shabinder.spotiflyer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@ -17,15 +24,25 @@ import androidx.compose.ui.platform.setContent
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.shabinder.spotiflyer.navigation.ComposeNavigation
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
|
||||
import com.shabinder.spotiflyer.ui.ComposeLearnTheme
|
||||
import com.shabinder.spotiflyer.ui.appNameStyle
|
||||
import com.shabinder.spotiflyer.utils.requestStoragePermission
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.chrisbanes.accompanist.insets.ProvideWindowInsets
|
||||
import dev.chrisbanes.accompanist.insets.statusBarsHeight
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Inject
|
||||
import com.shabinder.spotiflyer.utils.showDialog as showDialog1
|
||||
|
||||
/*
|
||||
* This is App's God Activity
|
||||
@ -34,13 +51,15 @@ import javax.inject.Inject
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private var spotifyService : SpotifyService? = null
|
||||
@Inject lateinit var moshi: Moshi
|
||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||
|
||||
// This app draws behind the system bars, so we want to handle fitting system windows
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent {
|
||||
ComposeLearnTheme {
|
||||
ProvideWindowInsets {
|
||||
@ -60,8 +79,103 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
initialize()
|
||||
}
|
||||
|
||||
private fun initialize() {
|
||||
authenticateSpotify()
|
||||
requestStoragePermission()
|
||||
disableDozeMode()
|
||||
//checkIfLatestVersion()
|
||||
createDirectories()
|
||||
handleIntentFromExternalActivity()
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
//Return to MainFragment For Further Processing of this Intent
|
||||
handleIntentFromExternalActivity(intent)
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
fun disableDozeMode() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val pm =
|
||||
this.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
val isIgnoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(packageName)
|
||||
if (!isIgnoringBatteryOptimizations) {
|
||||
val intent = Intent().apply{
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = Uri.parse("package:$packageName")
|
||||
}
|
||||
startActivityForResult(intent, 1233)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == 1233) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val pm =
|
||||
getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
val isIgnoringBatteryOptimizations =
|
||||
pm.isIgnoringBatteryOptimizations(packageName)
|
||||
if (isIgnoringBatteryOptimizations) {
|
||||
// Ignoring battery optimization
|
||||
} else {
|
||||
disableDozeMode()//Again Ask For Permission!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding my own new Spotify Web Api Requests!
|
||||
* */
|
||||
private fun implementSpotifyService(token: String) {
|
||||
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||
httpClient.addInterceptor(Interceptor { chain ->
|
||||
val request: Request =
|
||||
chain.request().newBuilder().addHeader(
|
||||
"Authorization",
|
||||
"Bearer $token"
|
||||
).build()
|
||||
chain.proceed(request)
|
||||
}).addInterceptor(NetworkInterceptor())
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://api.spotify.com/v1/")
|
||||
.client(httpClient.build())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
|
||||
spotifyService = retrofit.create(SpotifyService::class.java)
|
||||
sharedViewModel.spotifyService.value = spotifyService
|
||||
}
|
||||
|
||||
fun authenticateSpotify() {
|
||||
sharedViewModel.viewModelScope.launch {
|
||||
log("Spotify Authentication","Started")
|
||||
val token = spotifyServiceTokenRequest.getToken()
|
||||
token.value?.let {
|
||||
showDialog1("Success: Spotify Token Acquired")
|
||||
implementSpotifyService(it.access_token)
|
||||
}
|
||||
log("Spotify Token", token.value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun handleIntentFromExternalActivity(intent: Intent? = getIntent()) {
|
||||
if (intent?.action == Intent.ACTION_SEND) {
|
||||
if ("text/plain" == intent.type) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||
log("Intent Received", it)
|
||||
sharedViewModel.intentString.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object{
|
||||
|
@ -17,11 +17,21 @@
|
||||
|
||||
package com.shabinder.spotiflyer
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
|
||||
class SharedViewModel : ViewModel() {
|
||||
@ActivityRetainedScoped
|
||||
class SharedViewModel @ViewModelInject constructor(
|
||||
val databaseDAO: DatabaseDAO,
|
||||
val gaanaInterface : GaanaInterface,
|
||||
val ytDownloader: YoutubeDownloader
|
||||
) : ViewModel() {
|
||||
var intentString = MutableLiveData<String>()
|
||||
var spotifyService = MutableLiveData<SpotifyService>()
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.shabinder.spotiflyer.models
|
||||
|
||||
data class PlatformQueryResult(
|
||||
var folderType: String,
|
||||
var subFolder: String,
|
||||
var title: String,
|
||||
var coverUrl: String,
|
||||
var trackList: List<TrackDetails>
|
||||
)
|
@ -1,15 +1,17 @@
|
||||
package com.shabinder.spotiflyer.navigation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navArgument
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.compose.*
|
||||
import androidx.navigation.compose.popUpTo
|
||||
import com.shabinder.spotiflyer.ui.home.Home
|
||||
import com.shabinder.spotiflyer.ui.platforms.gaana.Gaana
|
||||
import com.shabinder.spotiflyer.ui.platforms.spotify.Spotify
|
||||
import com.shabinder.spotiflyer.ui.platforms.youtube.Youtube
|
||||
import com.shabinder.spotiflyer.utils.mainActivity
|
||||
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||
import com.shabinder.spotiflyer.utils.showDialog
|
||||
|
||||
@Composable
|
||||
fun ComposeNavigation() {
|
||||
@ -31,7 +33,7 @@ fun ComposeNavigation() {
|
||||
arguments = listOf(navArgument("link") { type = NavType.StringType })
|
||||
) {
|
||||
Spotify(
|
||||
link = it.arguments?.getString("link") ?: "error",
|
||||
fullLink = it.arguments?.getString("link") ?: "error",
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
@ -43,7 +45,7 @@ fun ComposeNavigation() {
|
||||
arguments = listOf(navArgument("link") { type = NavType.StringType })
|
||||
) {
|
||||
Gaana(
|
||||
link = it.arguments?.getString("link") ?: "error",
|
||||
fullLink = it.arguments?.getString("link") ?: "error",
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
@ -55,9 +57,42 @@ fun ComposeNavigation() {
|
||||
arguments = listOf(navArgument("link") { type = NavType.StringType })
|
||||
) {
|
||||
Youtube(
|
||||
link = it.arguments?.getString("link") ?: "error",
|
||||
fullLink = it.arguments?.getString("link") ?: "error",
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateToPlatform(link:String){
|
||||
when{
|
||||
//SPOTIFY
|
||||
link.contains("spotify",true) -> {
|
||||
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||
mainActivity.authenticateSpotify()
|
||||
}
|
||||
this.navigateAndPopUpToHome("spotify/$link")
|
||||
}
|
||||
|
||||
//YOUTUBE
|
||||
link.contains("youtube.com",true) || link.contains("youtu.be",true) -> {
|
||||
this.navigateAndPopUpToHome("youtube/$link")
|
||||
}
|
||||
|
||||
//GAANA
|
||||
link.contains("gaana",true) -> {
|
||||
this.navigateAndPopUpToHome("gaana/$link")
|
||||
}
|
||||
|
||||
else -> showDialog("Link is Not Valid")
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateAndPopUpToHome(route:String, inclusive:Boolean = false,singleInstance:Boolean = true){
|
||||
this.navigate(route){
|
||||
launchSingleTop = singleInstance
|
||||
popUpTo(route = "home"){
|
||||
this.inclusive = inclusive
|
||||
}
|
||||
}
|
||||
}
|
@ -17,17 +17,24 @@
|
||||
|
||||
package com.shabinder.spotiflyer.ui.base
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
abstract class TrackListViewModel:ViewModel() {
|
||||
abstract var folderType:String
|
||||
abstract var subFolder:String
|
||||
open val trackList = MutableLiveData<MutableList<TrackDetails>>()
|
||||
private val _trackList = MutableStateFlow<List<TrackDetails>>(mutableListOf())
|
||||
open val trackList:StateFlow<List<TrackDetails>>
|
||||
get() = _trackList
|
||||
|
||||
fun updateTrackList(list:List<TrackDetails>){
|
||||
_trackList.value = list
|
||||
}
|
||||
|
||||
private val loading = "Loading!"
|
||||
open var title = MutableLiveData<String>().apply { value = loading }
|
||||
open var coverUrl = MutableLiveData<String>()
|
||||
open var title = MutableStateFlow(loading)
|
||||
open var coverUrl = MutableStateFlow(loading)
|
||||
|
||||
}
|
@ -29,11 +29,16 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.navigate
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.navigation.navigateToPlatform
|
||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
||||
import com.shabinder.spotiflyer.ui.colorAccent
|
||||
import com.shabinder.spotiflyer.ui.colorPrimary
|
||||
import com.shabinder.spotiflyer.utils.mainActivity
|
||||
import com.shabinder.spotiflyer.utils.openPlatform
|
||||
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||
import com.shabinder.spotiflyer.utils.showDialog
|
||||
|
||||
@Composable
|
||||
fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
||||
@ -289,7 +294,7 @@ fun SearchPanel(
|
||||
)
|
||||
OutlinedButton(
|
||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||
onClick = {navController.navigate("track_list/$link") },
|
||||
onClick = {navController.navigateToPlatform(link)},
|
||||
border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent)))
|
||||
){
|
||||
Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp))
|
||||
|
@ -1,8 +1,45 @@
|
||||
package com.shabinder.spotiflyer.ui.platforms.gaana
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.navigation.NavController
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun Gaana(link: String, navController: NavController,) {
|
||||
fun Gaana(
|
||||
fullLink: String,
|
||||
navController: NavController
|
||||
) {
|
||||
//Coroutine Scope Active till this Composable is Active
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
//Link Schema: https://gaana.com/type/link
|
||||
val gaanaLink = fullLink.substringAfter("gaana.com/")
|
||||
|
||||
val link = gaanaLink.substringAfterLast('/', "error")
|
||||
val type = gaanaLink.substringBeforeLast('/', "error").substringAfterLast('/')
|
||||
|
||||
log("Gaana Fragment", "$type : $link")
|
||||
|
||||
//Error
|
||||
if (type == "Error" || link == "Error"){
|
||||
showDialog("Please Check Your Link!")
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
val result = gaanaSearch(
|
||||
type,
|
||||
link,
|
||||
sharedViewModel.gaanaInterface,
|
||||
sharedViewModel.databaseDAO,
|
||||
)
|
||||
|
||||
log("Gaana",result.toString())
|
||||
log("Gaana Tracks",result.trackList.size.toString())
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.gaana
|
||||
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.gaana.GaanaTrack
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||
import com.shabinder.spotiflyer.utils.queryActiveTracks
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
private const val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
|
||||
|
||||
suspend fun gaanaSearch(
|
||||
type:String,
|
||||
link:String,
|
||||
gaanaInterface: GaanaInterface,
|
||||
databaseDAO: DatabaseDAO,
|
||||
): PlatformQueryResult {
|
||||
val result = PlatformQueryResult(
|
||||
folderType = "",
|
||||
subFolder = link,
|
||||
title = link,
|
||||
coverUrl = gaanaPlaceholderImageUrl,
|
||||
trackList = listOf(),
|
||||
)
|
||||
with(result) {
|
||||
when (type) {
|
||||
"song" -> {
|
||||
gaanaInterface.getGaanaSong(seokey = link).value?.tracks?.firstOrNull()?.also {
|
||||
folderType = "Tracks"
|
||||
subFolder = ""
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
trackList = listOf(it).toTrackDetailsList(folderType, subFolder)
|
||||
title = it.track_title
|
||||
coverUrl = it.artworkLink
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Track",
|
||||
name = title,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"album" -> {
|
||||
gaanaInterface.getGaanaAlbum(seokey = link).value?.also {
|
||||
folderType = "Albums"
|
||||
subFolder = link
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
title = link
|
||||
coverUrl = it.custom_artworks.size_480p
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Album",
|
||||
name = title,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = trackList.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"playlist" -> {
|
||||
gaanaInterface.getGaanaPlaylist(seokey = link).value?.also {
|
||||
folderType = "Playlists"
|
||||
subFolder = link
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
title = link
|
||||
//coverUrl.value = "TODO"
|
||||
coverUrl = gaanaPlaceholderImageUrl
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Playlist",
|
||||
name = title,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = it.tracks.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"artist" -> {
|
||||
folderType = "Artist"
|
||||
subFolder = link
|
||||
coverUrl = gaanaPlaceholderImageUrl
|
||||
val artistDetails =
|
||||
gaanaInterface.getGaanaArtistDetails(seokey = link).value?.artist?.firstOrNull()
|
||||
?.also {
|
||||
title = it.name
|
||||
coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl
|
||||
}
|
||||
gaanaInterface.getGaanaArtistTracks(seokey = link).value?.also {
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Artist",
|
||||
name = artistDetails?.name ?: link,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = trackList.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {//TODO Handle Error}
|
||||
}
|
||||
}
|
||||
queryActiveTracks()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<GaanaTrack>.toTrackDetailsList(type:String , subFolder:String) = this.map {
|
||||
TrackDetails(
|
||||
title = it.track_title,
|
||||
artists = it.artist.map { artist -> artist?.name.toString() },
|
||||
durationSec = it.duration,
|
||||
albumArt = File(
|
||||
imageDir() + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
|
||||
albumName = it.album_title,
|
||||
year = it.release_date,
|
||||
comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}",
|
||||
trackUrl = it.lyrics_url,
|
||||
downloaded = it.downloaded ?: DownloadStatus.NotDownloaded,
|
||||
source = Source.Gaana,
|
||||
albumArtURL = it.artworkLink,
|
||||
outputFile = finalOutputDir(it.track_title,type, subFolder,".m4a")
|
||||
)
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.gaana
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.gaana.GaanaTrack
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.ui.base.TrackListViewModel
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||
import com.shabinder.spotiflyer.utils.queryActiveTracks
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class GaanaViewModel @ViewModelInject constructor(
|
||||
private val databaseDAO: DatabaseDAO,
|
||||
private val gaanaInterface : GaanaInterface
|
||||
) : TrackListViewModel(){
|
||||
|
||||
override var folderType:String = ""
|
||||
override var subFolder:String = ""
|
||||
|
||||
private val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
|
||||
|
||||
fun gaanaSearch(type:String,link:String){
|
||||
viewModelScope.launch {
|
||||
when (type) {
|
||||
"song" -> {
|
||||
gaanaInterface.getGaanaSong(seokey = link).value?.tracks?.firstOrNull()?.also {
|
||||
folderType = "Tracks"
|
||||
subFolder = ""
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
trackList.value = listOf(it).toTrackDetailsList(folderType, subFolder)
|
||||
title.value = it.track_title
|
||||
coverUrl.value = it.artworkLink
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Track",
|
||||
name = title.value!!,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl.value!!,
|
||||
totalFiles = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"album" -> {
|
||||
gaanaInterface.getGaanaAlbum(seokey = link).value?.also {
|
||||
folderType = "Albums"
|
||||
subFolder = link
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
title.value = link
|
||||
coverUrl.value = it.custom_artworks.size_480p
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Album",
|
||||
name = title.value!!,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = trackList.value?.size ?: 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"playlist" -> {
|
||||
gaanaInterface.getGaanaPlaylist(seokey = link).value?.also {
|
||||
folderType = "Playlists"
|
||||
subFolder = link
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
title.value = link
|
||||
//coverUrl.value = "TODO"
|
||||
coverUrl.value = gaanaPlaceholderImageUrl
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Playlist",
|
||||
name = title.value.toString(),
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = it.tracks.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"artist" -> {
|
||||
folderType = "Artist"
|
||||
subFolder = link
|
||||
val artistDetails =
|
||||
gaanaInterface.getGaanaArtistDetails(seokey = link).value?.artist?.firstOrNull()
|
||||
?.also {
|
||||
title.value = it.name
|
||||
coverUrl.value = it.artworkLink
|
||||
}
|
||||
gaanaInterface.getGaanaArtistTracks(seokey = link).value?.also {
|
||||
it.tracks.forEach { track ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
track.track_title,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
track.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
}
|
||||
trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Artist",
|
||||
name = artistDetails?.name ?: link,
|
||||
link = "https://gaana.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = trackList.value?.size ?: 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
queryActiveTracks()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<GaanaTrack>.toTrackDetailsList(type:String , subFolder:String) = this.map {
|
||||
TrackDetails(
|
||||
title = it.track_title,
|
||||
artists = it.artist.map { artist -> artist?.name.toString() },
|
||||
durationSec = it.duration,
|
||||
albumArt = File(
|
||||
imageDir() + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
|
||||
albumName = it.album_title,
|
||||
year = it.release_date,
|
||||
comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}",
|
||||
trackUrl = it.lyrics_url,
|
||||
downloaded = it.downloaded ?: DownloadStatus.NotDownloaded,
|
||||
source = Source.Gaana,
|
||||
albumArtURL = it.artworkLink,
|
||||
outputFile = finalOutputDir(it.track_title,type, subFolder,".m4a")
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
@ -1,8 +1,69 @@
|
||||
package com.shabinder.spotiflyer.ui.platforms.spotify
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.viewinterop.viewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavController
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun Spotify(link: String, navController: NavController,) {
|
||||
fun Spotify(fullLink: String, navController: NavController,) {
|
||||
val source: Source = Source.Spotify
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
var spotifyLink =
|
||||
"https://" + fullLink.substringAfterLast("https://").substringBefore(" ").trim()
|
||||
log("Spotify Fragment Link", spotifyLink)
|
||||
|
||||
coroutineScope.launch(Dispatchers.Default) {
|
||||
|
||||
/*
|
||||
* New Link Schema: https://link.tospotify.com/kqTBblrjQbb,
|
||||
* Fetching Standard Link: https://open.spotify.com/playlist/37i9dQZF1DX9RwfGbeGQwP?si=iWz7B1tETiunDntnDo3lSQ&_branch_match_id=862039436205270630
|
||||
* */
|
||||
if (!spotifyLink.contains("open.spotify")) {
|
||||
val resolvedLink = resolveLink(spotifyLink, sharedViewModel.gaanaInterface)
|
||||
log("Spotify Resolved Link", resolvedLink)
|
||||
spotifyLink = resolvedLink
|
||||
}
|
||||
|
||||
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
|
||||
val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
|
||||
|
||||
log("Spotify Fragment", "$type : $link")
|
||||
|
||||
if (sharedViewModel.spotifyService.value == null) {//Authentication pending!!
|
||||
if (isOnline()) mainActivity.authenticateSpotify()
|
||||
}
|
||||
|
||||
if (type == "Error" || link == "Error") {
|
||||
showDialog("Please Check Your Link!")
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
if (type == "episode" || type == "show") {
|
||||
//TODO Implementation
|
||||
showDialog("Implementing Soon, Stay Tuned!")
|
||||
} else {
|
||||
if (sharedViewModel.spotifyService.value == null){
|
||||
//Authentication Still Pending
|
||||
// TODO Better Implementation
|
||||
showDialog("Authentication Failed")
|
||||
navController.popBackStack()
|
||||
}else{
|
||||
val result = spotifySearch(
|
||||
type,
|
||||
link,
|
||||
sharedViewModel.spotifyService.value!!,
|
||||
sharedViewModel.databaseDAO
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.spotify
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Album
|
||||
import com.shabinder.spotiflyer.models.spotify.Image
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.models.spotify.Track
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
suspend fun spotifySearch(
|
||||
type:String,
|
||||
link: String,
|
||||
spotifyService: SpotifyService,
|
||||
databaseDAO: DatabaseDAO
|
||||
): PlatformQueryResult {
|
||||
val result = PlatformQueryResult(
|
||||
folderType = "",
|
||||
subFolder = "",
|
||||
title = "",
|
||||
coverUrl = "",
|
||||
trackList = listOf(),
|
||||
)
|
||||
with(result) {
|
||||
when (type) {
|
||||
"track" -> {
|
||||
spotifyService.getTrack(link).value?.also {
|
||||
folderType = "Tracks"
|
||||
subFolder = ""
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
trackList = listOf(it).toTrackDetailsList(folderType, subFolder)
|
||||
title = it.name.toString()
|
||||
coverUrl = (it.album?.images?.elementAtOrNull(1)?.url
|
||||
?: it.album?.images?.elementAtOrNull(0)?.url).toString()
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Track",
|
||||
name = title,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
val albumObject = spotifyService.getAlbum(link).value
|
||||
folderType = "Albums"
|
||||
subFolder = albumObject?.name.toString()
|
||||
albumObject?.tracks?.items?.forEach {
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
it.album = Album(
|
||||
images = listOf(
|
||||
Image(
|
||||
url = albumObject.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject.images?.elementAtOrNull(0)?.url
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
albumObject?.tracks?.items?.toTrackDetailsList(folderType, subFolder).let {
|
||||
if (it.isNullOrEmpty()) {
|
||||
//TODO Handle Error
|
||||
showDialog("Error Fetching Album")
|
||||
} else {
|
||||
trackList = it
|
||||
title = albumObject?.name.toString()
|
||||
coverUrl = (albumObject?.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject?.images?.elementAtOrNull(0)?.url).toString()
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Album",
|
||||
name = title,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = trackList.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
log("Spotify Service", spotifyService.toString())
|
||||
val playlistObject = spotifyService.getPlaylist(link).value
|
||||
folderType = "Playlists"
|
||||
subFolder = playlistObject?.name.toString()
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
log("Tracks Fetched", playlistObject?.tracks?.items?.size.toString())
|
||||
playlistObject?.tracks?.items?.forEach {
|
||||
it.track?.let { it1 ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it1.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it1.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
tempTrackList.add(it1)
|
||||
}
|
||||
}
|
||||
var moreTracksAvailable = !playlistObject?.tracks?.next.isNullOrBlank()
|
||||
|
||||
while (moreTracksAvailable) {
|
||||
//Check For More Tracks If available
|
||||
val moreTracks =
|
||||
spotifyService.getPlaylistTracks(link, offset = tempTrackList.size).value
|
||||
moreTracks?.items?.forEach {
|
||||
it.track?.let { it1 -> tempTrackList.add(it1) }
|
||||
}
|
||||
moreTracksAvailable = !moreTracks?.next.isNullOrBlank()
|
||||
}
|
||||
log("Total Tracks Fetched", tempTrackList.size.toString())
|
||||
trackList = tempTrackList.toTrackDetailsList(folderType, subFolder)
|
||||
title = playlistObject?.name.toString()
|
||||
coverUrl = playlistObject?.images?.elementAtOrNull(1)?.url
|
||||
?: playlistObject?.images?.firstOrNull()?.url.toString()
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Playlist",
|
||||
name = title,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl,
|
||||
totalFiles = tempTrackList.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
"episode" -> {//TODO
|
||||
}
|
||||
"show" -> {//TODO
|
||||
}
|
||||
else -> {
|
||||
//TODO Handle Error
|
||||
}
|
||||
}
|
||||
}
|
||||
queryActiveTracks()
|
||||
return result
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun resolveLink(
|
||||
url:String,
|
||||
gaanaInterface: GaanaInterface
|
||||
):String {
|
||||
val response = gaanaInterface.getResponse(url).execute().body()?.string().toString()
|
||||
val regex = """https://open\.spotify\.com.+\w""".toRegex()
|
||||
return regex.find(response)?.value.toString()
|
||||
}
|
||||
|
||||
private fun List<Track>.toTrackDetailsList(type:String, subFolder:String) = this.map {
|
||||
TrackDetails(
|
||||
title = it.name.toString(),
|
||||
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
|
||||
durationSec = (it.duration_ms/1000).toInt(),
|
||||
albumArt = File(
|
||||
Provider.imageDir() + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||
albumName = it.album?.name,
|
||||
year = it.album?.release_date,
|
||||
comment = "Genres:${it.album?.genres?.joinToString()}",
|
||||
trackUrl = it.href,
|
||||
downloaded = it.downloaded,
|
||||
source = Source.Spotify,
|
||||
albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString(),
|
||||
outputFile = finalOutputDir(it.name.toString(),type, subFolder,".m4a")
|
||||
)
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.spotify
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Album
|
||||
import com.shabinder.spotiflyer.models.spotify.Image
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.models.spotify.Track
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.ui.base.TrackListViewModel
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||
import com.shabinder.spotiflyer.utils.log
|
||||
import com.shabinder.spotiflyer.utils.queryActiveTracks
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class SpotifyViewModel @ViewModelInject constructor(
|
||||
private val databaseDAO: DatabaseDAO,
|
||||
private val gaanaInterface : GaanaInterface
|
||||
) : TrackListViewModel(){
|
||||
|
||||
override var folderType:String = ""
|
||||
override var subFolder:String = ""
|
||||
|
||||
var spotifyService : SpotifyService? = null
|
||||
|
||||
fun resolveLink(url:String):String {
|
||||
val response = gaanaInterface.getResponse(url).execute().body()?.string().toString()
|
||||
val regex = """https://open\.spotify\.com.+\w""".toRegex()
|
||||
return regex.find(response)?.value.toString()
|
||||
}
|
||||
|
||||
fun spotifySearch(type:String,link: String){
|
||||
viewModelScope.launch {
|
||||
when (type) {
|
||||
"track" -> {
|
||||
spotifyService?.getTrack(link)?.value?.also {
|
||||
folderType = "Tracks"
|
||||
subFolder = ""
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
trackList.value = listOf(it).toTrackDetailsList(folderType, subFolder)
|
||||
title.value = it.name
|
||||
coverUrl.value = it.album?.images?.elementAtOrNull(1)?.url
|
||||
?: it.album?.images?.elementAtOrNull(0)?.url
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Track",
|
||||
name = title.value.toString(),
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
val albumObject = spotifyService?.getAlbum(link)?.value
|
||||
folderType = "Albums"
|
||||
subFolder = albumObject?.name.toString()
|
||||
albumObject?.tracks?.items?.forEach {
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
it.album = Album(
|
||||
images = listOf(
|
||||
Image(
|
||||
url = albumObject.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject.images?.elementAtOrNull(0)?.url
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
trackList.value = albumObject?.tracks?.items?.toTrackDetailsList(folderType, subFolder)
|
||||
title.value = albumObject?.name
|
||||
coverUrl.value = albumObject?.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject?.images?.elementAtOrNull(0)?.url
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Album",
|
||||
name = title.value.toString(),
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = trackList.value?.size ?: 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
log("Spotify Service",spotifyService.toString())
|
||||
val playlistObject = spotifyService?.getPlaylist(link)?.value
|
||||
folderType = "Playlists"
|
||||
subFolder = playlistObject?.name.toString()
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
log("Tracks Fetched", playlistObject?.tracks?.items?.size.toString())
|
||||
playlistObject?.tracks?.items?.forEach {
|
||||
it.track?.let { it1 ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it1.name.toString(),
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it1.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
tempTrackList.add(it1)
|
||||
}
|
||||
}
|
||||
var moreTracksAvailable = !playlistObject?.tracks?.next.isNullOrBlank()
|
||||
|
||||
while (moreTracksAvailable) {
|
||||
//Check For More Tracks If available
|
||||
val moreTracks = spotifyService?.getPlaylistTracks(link, offset = tempTrackList.size)?.value
|
||||
moreTracks?.items?.forEach {
|
||||
it.track?.let { it1 -> tempTrackList.add(it1) }
|
||||
}
|
||||
moreTracksAvailable = !moreTracks?.next.isNullOrBlank()
|
||||
}
|
||||
log("Total Tracks Fetched", tempTrackList.size.toString())
|
||||
trackList.value = tempTrackList.toTrackDetailsList(folderType, subFolder)
|
||||
title.value = playlistObject?.name
|
||||
coverUrl.value = playlistObject?.images?.elementAtOrNull(1)?.url
|
||||
?: playlistObject?.images?.firstOrNull()?.url.toString()
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Playlist",
|
||||
name = title.value.toString(),
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = tempTrackList.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
"episode" -> {//TODO
|
||||
}
|
||||
"show" -> {//TODO
|
||||
}
|
||||
}
|
||||
queryActiveTracks()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Track>.toTrackDetailsList(type:String , subFolder:String) = this.map {
|
||||
TrackDetails(
|
||||
title = it.name.toString(),
|
||||
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
|
||||
durationSec = (it.duration_ms/1000).toInt(),
|
||||
albumArt = File(
|
||||
imageDir() + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||
albumName = it.album?.name,
|
||||
year = it.album?.release_date,
|
||||
comment = "Genres:${it.album?.genres?.joinToString()}",
|
||||
trackUrl = it.href,
|
||||
downloaded = it.downloaded,
|
||||
source = Source.Spotify,
|
||||
albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString(),
|
||||
outputFile = finalOutputDir(it.name.toString(),type, subFolder,".m4a")
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
@ -1,9 +1,52 @@
|
||||
package com.shabinder.spotiflyer.ui.platforms.youtube
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.navigation.NavController
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||
import com.shabinder.spotiflyer.utils.showDialog
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
private const val sampleDomain2 = "youtu.be"
|
||||
private const val sampleDomain1 = "youtube.com"
|
||||
|
||||
@Composable
|
||||
fun Youtube(link: String, navController: NavController,) {
|
||||
fun Youtube(fullLink: String, navController: NavController,) {
|
||||
val source = Source.YouTube
|
||||
|
||||
//Coroutine Scope Active till this Composable is Active
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
coroutineScope.launch {
|
||||
val link = fullLink.removePrefix("https://").removePrefix("http://")
|
||||
if(link.contains("playlist",true) || link.contains("list",true)){
|
||||
// Given Link is of a Playlist
|
||||
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
|
||||
getYTPlaylist(
|
||||
playlistId,
|
||||
sharedViewModel.ytDownloader,
|
||||
sharedViewModel.databaseDAO
|
||||
)
|
||||
}else{//Given Link is of a Video
|
||||
var searchId = "error"
|
||||
if(link.contains(sampleDomain1,true) ){
|
||||
searchId = link.substringAfterLast("=","error")
|
||||
}
|
||||
if(link.contains(sampleDomain2,true) ){
|
||||
searchId = link.substringAfterLast("/","error")
|
||||
}
|
||||
if(searchId != "error") {
|
||||
getYTTrack(
|
||||
searchId,
|
||||
sharedViewModel.ytDownloader,
|
||||
sharedViewModel.databaseDAO
|
||||
)
|
||||
}else{
|
||||
showDialog("Your Youtube Link is not of a Video!!")
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.youtube
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
* YT Album Art Schema
|
||||
* HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||
* Normal Url: https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||
* */
|
||||
|
||||
suspend fun getYTPlaylist(
|
||||
searchId: String,
|
||||
ytDownloader: YoutubeDownloader,
|
||||
databaseDAO: DatabaseDAO,
|
||||
):PlatformQueryResult{
|
||||
val result = PlatformQueryResult(
|
||||
folderType = "",
|
||||
subFolder = "",
|
||||
title = "",
|
||||
coverUrl = "",
|
||||
trackList = listOf(),
|
||||
)
|
||||
with(result) {
|
||||
try {
|
||||
log("YT Playlist", searchId)
|
||||
val playlist = ytDownloader.getPlaylist(searchId)
|
||||
val playlistDetails = playlist.details()
|
||||
val name = playlistDetails.title()
|
||||
subFolder = removeIllegalChars(name)
|
||||
val videos = playlist.videos()
|
||||
|
||||
coverUrl = "https://i.ytimg.com/vi/${
|
||||
videos.firstOrNull()?.videoId()
|
||||
}/hqdefault.jpg"
|
||||
title = name
|
||||
|
||||
trackList = videos.map {
|
||||
TrackDetails(
|
||||
title = it.title(),
|
||||
artists = listOf(it.author().toString()),
|
||||
durationSec = it.lengthSeconds(),
|
||||
albumArt = File(
|
||||
imageDir() + it.videoId() + ".jpeg"
|
||||
),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg",
|
||||
downloaded = if (File(
|
||||
finalOutputDir(
|
||||
itemName = it.title(),
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)
|
||||
).exists()
|
||||
)
|
||||
DownloadStatus.Downloaded
|
||||
else {
|
||||
DownloadStatus.NotDownloaded
|
||||
},
|
||||
outputFile = finalOutputDir(it.title(), folderType, subFolder, ".m4a"),
|
||||
videoID = it.videoId()
|
||||
)
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "PlayList",
|
||||
name = if (name.length > 17) {
|
||||
"${name.subSequence(0, 16)}..."
|
||||
} else {
|
||||
name
|
||||
},
|
||||
link = "https://www.youtube.com/playlist?list=$searchId",
|
||||
coverUrl = "https://i.ytimg.com/vi/${
|
||||
videos.firstOrNull()?.videoId()
|
||||
}/hqdefault.jpg",
|
||||
totalFiles = videos.size,
|
||||
)
|
||||
)
|
||||
}
|
||||
queryActiveTracks()
|
||||
} catch (e: Exception) {
|
||||
showDialog("An Error Occurred While Processing!")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
suspend fun getYTTrack(
|
||||
searchId:String,
|
||||
ytDownloader: YoutubeDownloader,
|
||||
databaseDAO: DatabaseDAO
|
||||
):PlatformQueryResult {
|
||||
val result = PlatformQueryResult(
|
||||
folderType = "",
|
||||
subFolder = "",
|
||||
title = "",
|
||||
coverUrl = "",
|
||||
trackList = listOf(),
|
||||
)
|
||||
with(result) {
|
||||
try {
|
||||
log("YT Video", searchId)
|
||||
val video = ytDownloader.getVideo(searchId)
|
||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||
val detail = video?.details()
|
||||
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(), "", true)
|
||||
?: detail?.title() ?: ""
|
||||
log("YT View Model", detail.toString())
|
||||
trackList = listOf(
|
||||
TrackDetails(
|
||||
title = name,
|
||||
artists = listOf(detail?.author().toString()),
|
||||
durationSec = detail?.lengthSeconds() ?: 0,
|
||||
albumArt = File(imageDir(), "$searchId.jpeg"),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
|
||||
downloaded = if (File(
|
||||
finalOutputDir(
|
||||
itemName = name,
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)
|
||||
).exists()
|
||||
)
|
||||
DownloadStatus.Downloaded
|
||||
else {
|
||||
DownloadStatus.NotDownloaded
|
||||
},
|
||||
outputFile = finalOutputDir(name, folderType, subFolder, ".m4a"),
|
||||
videoID = searchId
|
||||
)
|
||||
)
|
||||
title = name
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Track",
|
||||
name = if (name.length > 17) {
|
||||
"${name.subSequence(0, 16)}..."
|
||||
} else {
|
||||
name
|
||||
},
|
||||
link = "https://www.youtube.com/watch?v=$searchId",
|
||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
|
||||
totalFiles = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
queryActiveTracks()
|
||||
} catch (e: Exception) {
|
||||
showDialog("An Error Occurred While Processing!")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Shabinder Singh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.platforms.youtube
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.ui.base.TrackListViewModel
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class YoutubeViewModel @ViewModelInject constructor(
|
||||
private val databaseDAO: DatabaseDAO,
|
||||
private val ytDownloader: YoutubeDownloader
|
||||
) : TrackListViewModel(){
|
||||
/*
|
||||
* YT Album Art Schema
|
||||
* HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||
* Normal Url: https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||
* */
|
||||
|
||||
override var folderType = "YT_Downloads"
|
||||
override var subFolder = ""
|
||||
|
||||
fun getYTPlaylist(searchId:String){
|
||||
if(!isOnline())return
|
||||
try{
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
log("YT Playlist",searchId)
|
||||
val playlist = ytDownloader.getPlaylist(searchId)
|
||||
val playlistDetails = playlist.details()
|
||||
val name = playlistDetails.title()
|
||||
subFolder = removeIllegalChars(name)
|
||||
val videos = playlist.videos()
|
||||
coverUrl.postValue("https://i.ytimg.com/vi/${videos.firstOrNull()?.videoId()}/hqdefault.jpg")
|
||||
title.postValue(
|
||||
if(name.length > 17){"${name.subSequence(0,16)}..."}else{name}
|
||||
)
|
||||
this@YoutubeViewModel.trackList.postValue(videos.map {
|
||||
TrackDetails(
|
||||
title = it.title(),
|
||||
artists = listOf(it.author().toString()),
|
||||
durationSec = it.lengthSeconds(),
|
||||
albumArt = File(
|
||||
imageDir() + it.videoId() + ".jpeg"
|
||||
),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg",
|
||||
downloaded = if (File(
|
||||
finalOutputDir(
|
||||
itemName = it.title(),
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)).exists()
|
||||
)
|
||||
DownloadStatus.Downloaded
|
||||
else {
|
||||
DownloadStatus.NotDownloaded
|
||||
},
|
||||
outputFile = finalOutputDir(it.title(),folderType, subFolder,".m4a"),
|
||||
videoID = it.videoId()
|
||||
)
|
||||
}.toMutableList())
|
||||
|
||||
withContext(Dispatchers.IO){
|
||||
databaseDAO.insert(DownloadRecord(
|
||||
type = "PlayList",
|
||||
name = if(name.length > 17){"${name.subSequence(0,16)}..."}else{name},
|
||||
link = "https://www.youtube.com/playlist?list=$searchId",
|
||||
coverUrl = "https://i.ytimg.com/vi/${videos.firstOrNull()?.videoId()}/hqdefault.jpg",
|
||||
totalFiles = videos.size,
|
||||
))
|
||||
}
|
||||
queryActiveTracks()
|
||||
}
|
||||
}catch (e:Exception){
|
||||
showDialog("An Error Occurred While Processing!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
fun getYTTrack(searchId:String) {
|
||||
if(!isOnline())return
|
||||
try{
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
log("YT Video",searchId)
|
||||
val video = ytDownloader.getVideo(searchId)
|
||||
coverUrl.postValue("https://i.ytimg.com/vi/$searchId/hqdefault.jpg")
|
||||
val detail = video?.details()
|
||||
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title() ?: ""
|
||||
log("YT View Model",detail.toString())
|
||||
this@YoutubeViewModel.trackList.postValue(
|
||||
listOf(
|
||||
TrackDetails(
|
||||
title = name,
|
||||
artists = listOf(detail?.author().toString()),
|
||||
durationSec = detail?.lengthSeconds()?:0,
|
||||
albumArt = File(imageDir(),"$searchId.jpeg"),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
|
||||
downloaded = if (File(
|
||||
finalOutputDir(
|
||||
itemName = name,
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)).exists()
|
||||
)
|
||||
DownloadStatus.Downloaded
|
||||
else {
|
||||
DownloadStatus.NotDownloaded
|
||||
},
|
||||
outputFile = finalOutputDir(name,folderType, subFolder,".m4a"),
|
||||
videoID = searchId
|
||||
)
|
||||
).toMutableList()
|
||||
)
|
||||
title.postValue(
|
||||
if(name.length > 17){"${name.subSequence(0,16)}..."}else{name}
|
||||
)
|
||||
|
||||
withContext(Dispatchers.IO){
|
||||
databaseDAO.insert(DownloadRecord(
|
||||
type = "Track",
|
||||
name = if(name.length > 17){"${name.subSequence(0,16)}..."}else{name},
|
||||
link = "https://www.youtube.com/watch?v=$searchId",
|
||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg",
|
||||
totalFiles = 1,
|
||||
))
|
||||
}
|
||||
queryActiveTracks()
|
||||
}
|
||||
} catch (e:Exception){
|
||||
showDialog("An Error Occurred While Processing!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ import java.io.File
|
||||
val mainActivity
|
||||
get() = MainActivity.getInstance()
|
||||
|
||||
val sharedViewModel
|
||||
get() = MainActivity.getSharedViewModel()
|
||||
|
||||
fun loadAllImages(context: Context? = mainActivity, images:List<String>? = null,source: Source) {
|
||||
val serviceIntent = Intent(context, ForegroundService::class.java)
|
||||
images?.let { serviceIntent.putStringArrayListExtra("imagesList",(it + source.name) as ArrayList<String>) }
|
||||
|
Loading…
Reference in New Issue
Block a user