mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
Dependencies Handling at Lower Level.
This commit is contained in:
parent
c58f73be1d
commit
04b6f13b22
@ -36,6 +36,7 @@ import androidx.navigation.findNavController
|
||||
import com.github.javiersantos.appupdater.AppUpdater
|
||||
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
||||
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
@ -57,9 +58,9 @@ import javax.inject.Inject
|
||||
class MainActivity : AppCompatActivity(){
|
||||
private var spotifyService : SpotifyService? = null
|
||||
private lateinit var binding: MainActivityBinding
|
||||
lateinit var snackBarAnchor: View
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
private lateinit var navController: NavController
|
||||
lateinit var snackBarAnchor: View
|
||||
lateinit var navController: NavController
|
||||
@Inject lateinit var moshi: Moshi
|
||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||
|
||||
@ -72,6 +73,7 @@ class MainActivity : AppCompatActivity(){
|
||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||
navController = findNavController(R.id.navHostFragment)
|
||||
snackBarAnchor = binding.snackBarPosition
|
||||
DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi
|
||||
|
||||
authenticateSpotify()
|
||||
|
||||
@ -80,7 +82,6 @@ class MainActivity : AppCompatActivity(){
|
||||
checkIfLatestVersion()
|
||||
createDirectories()
|
||||
Log.i("Connection Status", isOnline().toString())
|
||||
|
||||
//starting Notification and Downloader Service!
|
||||
startService(this)
|
||||
|
||||
|
@ -17,14 +17,18 @@
|
||||
|
||||
package com.shabinder.spotiflyer
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
class SharedViewModel : ViewModel() {
|
||||
class SharedViewModel @ViewModelInject constructor(
|
||||
val youtubeMusicApi: YoutubeMusicApi
|
||||
) : ViewModel() {
|
||||
var intentString = MutableLiveData<String>()
|
||||
var spotifyService = MutableLiveData<SpotifyService>()
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package com.shabinder.spotiflyer.recyclerView
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -46,6 +47,7 @@ class TrackListAdapter(private val viewModel :TrackListViewModel): ListAdapter<T
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
if(itemCount == 1){ holder.binding.imageUrl.visibility = View.GONE}else{
|
||||
@ -100,8 +102,8 @@ class TrackListAdapter(private val viewModel :TrackListViewModel): ListAdapter<T
|
||||
}
|
||||
}
|
||||
|
||||
holder.binding.trackName.text = "${if(item.title.length > 17){"${item.title.subSequence(0,16)}..."}else{item.title}}"
|
||||
holder.binding.artist.text = "${item.artists.get(0)}..."
|
||||
holder.binding.trackName.text = if(item.title.length > 20){"${item.title.subSequence(0,18)}..."}else{item.title}
|
||||
holder.binding.artist.text = "${item.artists.firstOrNull()}..."
|
||||
holder.binding.duration.text = "${item.durationSec/60} minutes, ${item.durationSec%60} sec"
|
||||
}
|
||||
|
||||
|
@ -24,25 +24,18 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GaanaFragment : TrackListFragment<GaanaViewModel,GaanaFragmentArgs>() {
|
||||
|
||||
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
||||
@Inject lateinit var gaanaInterface: GaanaInterface
|
||||
override lateinit var viewModel: GaanaViewModel
|
||||
override lateinit var adapter: TrackListAdapter
|
||||
override var source: Source = Source.Gaana
|
||||
@ -53,8 +46,8 @@ class GaanaFragment : TrackListFragment<GaanaViewModel,GaanaFragmentArgs>() {
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
initializeAll()
|
||||
viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java)
|
||||
adapter = TrackListAdapter(viewModel)
|
||||
|
||||
val gaanaLink = GaanaFragmentArgs.fromBundle(requireArguments()).link.substringAfter("gaana.com/")
|
||||
//Link Schema: https://gaana.com/type/link
|
||||
@ -112,21 +105,4 @@ class GaanaFragment : TrackListFragment<GaanaViewModel,GaanaFragmentArgs>() {
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Initialization
|
||||
**/
|
||||
private fun initializeAll() {
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java)
|
||||
viewModel.gaanaInterface = gaanaInterface
|
||||
adapter = TrackListAdapter(viewModel)
|
||||
DownloadHelper.youtubeMusicApi = youtubeMusicApi
|
||||
DownloadHelper.sharedViewModel = sharedViewModel
|
||||
DownloadHelper.statusBar = binding.statusBar
|
||||
binding.trackList.adapter = adapter
|
||||
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -18,13 +18,12 @@
|
||||
package com.shabinder.spotiflyer.ui.gaana
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
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.*
|
||||
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
|
||||
@ -35,18 +34,20 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
|
||||
class GaanaViewModel @ViewModelInject constructor(
|
||||
val databaseDAO: DatabaseDAO,
|
||||
val gaanaInterface : GaanaInterface
|
||||
) : TrackListViewModel(){
|
||||
|
||||
override var folderType:String = ""
|
||||
override var subFolder:String = ""
|
||||
var gaanaInterface : GaanaInterface? = null
|
||||
val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
|
||||
private val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
|
||||
|
||||
fun gaanaSearch(type:String,link:String){
|
||||
when(type){
|
||||
"song" -> {
|
||||
uiScope.launch {
|
||||
getGaanaSong(link)?.tracks?.firstOrNull()?.also {
|
||||
gaanaInterface.getGaanaSong(seokey = link).value?.tracks?.firstOrNull()?.also {
|
||||
folderType = "Tracks"
|
||||
if(File(finalOutputDir(it.track_title,folderType,subFolder)).exists()){//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
@ -72,7 +73,7 @@ class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO)
|
||||
}
|
||||
"album" -> {
|
||||
uiScope.launch {
|
||||
getGaanaAlbum(link)?.also {
|
||||
gaanaInterface.getGaanaAlbum(seokey = link).value?.also {
|
||||
folderType = "Albums"
|
||||
subFolder = link
|
||||
it.tracks.forEach { track ->
|
||||
@ -99,7 +100,7 @@ class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO)
|
||||
}
|
||||
"playlist" -> {
|
||||
uiScope.launch {
|
||||
getGaanaPlaylist(link)?.also {
|
||||
gaanaInterface.getGaanaPlaylist(seokey = link).value?.also {
|
||||
folderType = "Playlists"
|
||||
subFolder = link
|
||||
it.tracks.forEach {track ->
|
||||
@ -129,11 +130,11 @@ class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO)
|
||||
uiScope.launch {
|
||||
folderType = "Artist"
|
||||
subFolder = link
|
||||
val artistDetails = getGaanaArtistDetails(link)?.artist?.firstOrNull()?.also {
|
||||
val artistDetails = gaanaInterface.getGaanaArtistDetails(seokey = link).value?.artist?.firstOrNull()?.also {
|
||||
title.value = it.name
|
||||
coverUrl.value = it.artworkLink
|
||||
}
|
||||
getGaanaArtistTracks(link)?.also {
|
||||
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
|
||||
@ -175,25 +176,4 @@ class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO)
|
||||
albumArtURL = it.artworkLink
|
||||
)
|
||||
}.toMutableList()
|
||||
|
||||
private suspend fun getGaanaSong(songLink:String): GaanaSong?{
|
||||
Log.i("Requesting","https://gaana.com/song/$songLink")
|
||||
return gaanaInterface?.getGaanaSong(seokey = songLink)?.value
|
||||
}
|
||||
private suspend fun getGaanaAlbum(albumLink:String): GaanaAlbum?{
|
||||
Log.i("Requesting","https://gaana.com/album/$albumLink")
|
||||
return gaanaInterface?.getGaanaAlbum(seokey = albumLink)?.value
|
||||
}
|
||||
private suspend fun getGaanaPlaylist(link:String): GaanaPlaylist?{
|
||||
Log.i("Requesting","https://gaana.com/playlist/$link")
|
||||
return gaanaInterface?.getGaanaPlaylist(seokey = link)?.value
|
||||
}
|
||||
private suspend fun getGaanaArtistDetails(link:String): GaanaArtistDetails?{
|
||||
Log.i("Requesting","https://gaana.com/artist/$link")
|
||||
return gaanaInterface?.getGaanaArtistDetails(seokey = link)?.value
|
||||
}
|
||||
private suspend fun getGaanaArtistTracks(link:String,limit:Int = 50): GaanaArtistTracks?{
|
||||
Log.i("Requesting","Tracks of: https://gaana.com/artist/$link")
|
||||
return gaanaInterface?.getGaanaArtistTracks(seokey = link,limit = limit)?.value
|
||||
}
|
||||
}
|
@ -25,23 +25,19 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.shabinder.spotiflyer.utils.Provider.mainActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SpotifyFragment : TrackListFragment<SpotifyViewModel,SpotifyFragmentArgs>() {
|
||||
|
||||
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
||||
override lateinit var viewModel: SpotifyViewModel
|
||||
override lateinit var adapter: TrackListAdapter
|
||||
override var source: Source = Source.Spotify
|
||||
@ -132,10 +128,5 @@ class SpotifyFragment : TrackListFragment<SpotifyViewModel,SpotifyFragmentArgs>(
|
||||
sharedViewModel.spotifyService.observe(viewLifecycleOwner, {
|
||||
this.viewModel.spotifyService = it
|
||||
})
|
||||
DownloadHelper.youtubeMusicApi = youtubeMusicApi
|
||||
DownloadHelper.sharedViewModel = sharedViewModel
|
||||
DownloadHelper.statusBar = binding.statusBar
|
||||
binding.trackList.adapter = adapter
|
||||
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
}
|
@ -24,7 +24,10 @@ 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.*
|
||||
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.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.Provider
|
||||
import com.shabinder.spotiflyer.utils.TrackListViewModel
|
||||
@ -34,7 +37,9 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
|
||||
class SpotifyViewModel @ViewModelInject constructor(
|
||||
val databaseDAO: DatabaseDAO,
|
||||
) : TrackListViewModel(){
|
||||
|
||||
override var folderType:String = ""
|
||||
override var subFolder:String = ""
|
||||
@ -42,105 +47,148 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
var spotifyService : SpotifyService? = null
|
||||
|
||||
fun spotifySearch(type:String,link: String){
|
||||
when (type) {
|
||||
"track" -> {
|
||||
uiScope.launch {
|
||||
getTrackDetails(link)?.also {
|
||||
uiScope.launch {
|
||||
when (type) {
|
||||
"track" -> {
|
||||
spotifyService?.getTrack(link)?.value?.also {
|
||||
folderType = "Tracks"
|
||||
if(File(finalOutputDir(it.name,folderType,subFolder)).exists()){//Download Already Present!!
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
trackList.value = listOf(it).toTrackDetailsList()
|
||||
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!!,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value!!,
|
||||
totalFiles = 1,
|
||||
downloaded = it.downloaded == DownloadStatus.Downloaded,
|
||||
directory = finalOutputDir(it.name,folderType,subFolder)
|
||||
))
|
||||
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!!,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value!!,
|
||||
totalFiles = 1,
|
||||
downloaded = it.downloaded == DownloadStatus.Downloaded,
|
||||
directory = finalOutputDir(it.name, folderType, subFolder)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
uiScope.launch {
|
||||
val albumObject = getAlbumDetails(link)
|
||||
"album" -> {
|
||||
val albumObject = spotifyService?.getAlbum(link)?.value
|
||||
folderType = "Albums"
|
||||
subFolder = albumObject?.name.toString()
|
||||
albumObject?.tracks?.items?.forEach {
|
||||
if(File(finalOutputDir(it.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it.name!!,
|
||||
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 )))
|
||||
it.album = Album(
|
||||
images = listOf(
|
||||
Image(
|
||||
url = albumObject.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject.images?.elementAtOrNull(0)?.url
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
trackList.value = albumObject?.tracks?.items?.toTrackDetailsList()
|
||||
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!!,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = trackList.value?.size ?: 0,
|
||||
downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == trackList.value?.size,
|
||||
directory = finalOutputDir(type = folderType,subFolder = subFolder)
|
||||
))
|
||||
coverUrl.value = albumObject?.images?.elementAtOrNull(1)?.url
|
||||
?: albumObject?.images?.elementAtOrNull(0)?.url
|
||||
withContext(Dispatchers.IO) {
|
||||
databaseDAO.insert(
|
||||
DownloadRecord(
|
||||
type = "Album",
|
||||
name = title.value!!,
|
||||
link = "https://open.spotify.com/$type/$link",
|
||||
coverUrl = coverUrl.value.toString(),
|
||||
totalFiles = trackList.value?.size ?: 0,
|
||||
downloaded = File(
|
||||
finalOutputDir(
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)
|
||||
).listFiles()?.size == trackList.value?.size,
|
||||
directory = finalOutputDir(type = folderType, subFolder = subFolder)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
uiScope.launch {
|
||||
val playlistObject = getPlaylistDetails(link)
|
||||
"playlist" -> {
|
||||
val playlistObject = spotifyService?.getPlaylist(link)?.value
|
||||
folderType = "Playlists"
|
||||
subFolder = playlistObject?.name.toString()
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
Log.i("Tracks Fetched",playlistObject?.tracks?.items?.size.toString())
|
||||
Log.i("Tracks Fetched", playlistObject?.tracks?.items?.size.toString())
|
||||
playlistObject?.tracks?.items?.forEach {
|
||||
it.track?.let {
|
||||
it1 -> if(File(finalOutputDir(it1.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||
it1.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
it.track?.let { it1 ->
|
||||
if (File(
|
||||
finalOutputDir(
|
||||
it1.name!!,
|
||||
folderType,
|
||||
subFolder
|
||||
)
|
||||
).exists()
|
||||
) {//Download Already Present!!
|
||||
it1.downloaded = DownloadStatus.Downloaded
|
||||
}
|
||||
tempTrackList.add(it1)
|
||||
}
|
||||
}
|
||||
var moreTracksAvailable = !playlistObject?.tracks?.next.isNullOrBlank()
|
||||
|
||||
while(moreTracksAvailable){
|
||||
while (moreTracksAvailable) {
|
||||
//Check For More Tracks If available
|
||||
val moreTracks = getPlaylistTrackDetails(link,offset = tempTrackList.size)
|
||||
moreTracks?.items?.forEach{
|
||||
val moreTracks = spotifyService?.getPlaylistTracks(link, offset = tempTrackList.size)?.value
|
||||
moreTracks?.items?.forEach {
|
||||
it.track?.let { it1 -> tempTrackList.add(it1) }
|
||||
}
|
||||
moreTracksAvailable = !moreTracks?.next.isNullOrBlank()
|
||||
}
|
||||
Log.i("Total Tracks Fetched",tempTrackList.size.toString())
|
||||
Log.i("Total Tracks Fetched", tempTrackList.size.toString())
|
||||
trackList.value = tempTrackList.toTrackDetailsList()
|
||||
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,
|
||||
downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == tempTrackList.size,
|
||||
directory = finalOutputDir(type = folderType,subFolder = subFolder)
|
||||
))
|
||||
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,
|
||||
downloaded = File(
|
||||
finalOutputDir(
|
||||
type = folderType,
|
||||
subFolder = subFolder
|
||||
)
|
||||
).listFiles()?.size == tempTrackList.size,
|
||||
directory = finalOutputDir(type = folderType, subFolder = subFolder)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"episode" -> {//TODO
|
||||
}
|
||||
"show" -> {//TODO
|
||||
"episode" -> {//TODO
|
||||
}
|
||||
"show" -> {//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,21 +211,4 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()
|
||||
)
|
||||
}.toMutableList()
|
||||
|
||||
private suspend fun getTrackDetails(trackLink:String): Track?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/tracks/$trackLink")
|
||||
return spotifyService?.getTrack(trackLink)?.value
|
||||
}
|
||||
private suspend fun getAlbumDetails(albumLink:String): Album?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/albums/$albumLink")
|
||||
return spotifyService?.getAlbum(albumLink)?.value
|
||||
}
|
||||
private suspend fun getPlaylistDetails(link:String): Playlist?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link")
|
||||
return spotifyService?.getPlaylist(link)?.value
|
||||
}
|
||||
private suspend fun getPlaylistTrackDetails(link:String,offset:Int = 0,limit:Int = 100): PagingObjectPlaylistTrack?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link/tracks?offset=$offset&limit=$limit")
|
||||
return spotifyService?.getPlaylistTracks(link, offset, limit)?.value
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
@ -32,7 +31,6 @@ import com.shabinder.spotiflyer.utils.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val sampleDomain2 = "youtu.be"
|
||||
private const val sampleDomain1 = "youtube.com"
|
||||
@ -40,7 +38,6 @@ private const val sampleDomain1 = "youtube.com"
|
||||
@AndroidEntryPoint
|
||||
class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>() {
|
||||
|
||||
@Inject lateinit var ytDownloader: YoutubeDownloader
|
||||
override lateinit var viewModel: YoutubeViewModel
|
||||
override lateinit var adapter : TrackListAdapter
|
||||
override var source: Source = Source.YouTube
|
||||
@ -53,7 +50,6 @@ class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>(
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
this.viewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
||||
adapter = TrackListAdapter(this.viewModel)
|
||||
binding.trackList.adapter = adapter
|
||||
|
||||
val args = YoutubeFragmentArgs.fromBundle(requireArguments())
|
||||
val link = args.link
|
||||
@ -66,7 +62,7 @@ class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>(
|
||||
if(link.contains("playlist",true) || link.contains("list",true)){
|
||||
// Given Link is of a Playlist
|
||||
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
|
||||
this.viewModel.getYTPlaylist(playlistId,ytDownloader)
|
||||
this.viewModel.getYTPlaylist(playlistId)
|
||||
}else{//Given Link is of a Video
|
||||
var searchId = "error"
|
||||
if(link.contains(sampleDomain1,true) ){
|
||||
@ -76,7 +72,7 @@ class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>(
|
||||
searchId = link.substringAfterLast("/","error")
|
||||
}
|
||||
if(searchId != "error") {
|
||||
this.viewModel.getYTTrack(searchId,ytDownloader)
|
||||
this.viewModel.getYTTrack(searchId)
|
||||
}else{showMessage("Your Youtube Link is not of a Video!!")}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,10 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
|
||||
class YoutubeViewModel @ViewModelInject constructor(
|
||||
val databaseDAO: DatabaseDAO,
|
||||
val ytDownloader: YoutubeDownloader
|
||||
) : TrackListViewModel(){
|
||||
/*
|
||||
* YT Album Art Schema
|
||||
* HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||
@ -44,7 +47,7 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
override var folderType = "YT_Downloads"
|
||||
override var subFolder = ""
|
||||
|
||||
fun getYTPlaylist(searchId:String, ytDownloader:YoutubeDownloader){
|
||||
fun getYTPlaylist(searchId:String){
|
||||
if(!isOnline())return
|
||||
try{
|
||||
uiScope.launch(Dispatchers.IO) {
|
||||
@ -102,7 +105,7 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
fun getYTTrack(searchId:String, ytDownloader:YoutubeDownloader) {
|
||||
fun getYTTrack(searchId:String) {
|
||||
if(!isOnline())return
|
||||
try{
|
||||
uiScope.launch(Dispatchers.IO) {
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
package com.shabinder.spotiflyer.utils
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
|
||||
@ -27,13 +27,14 @@ const val NoInternetErrorCode = 222
|
||||
|
||||
class NetworkInterceptor: Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
Log.i("Network Requesting",chain.request().url.toString())
|
||||
return if (!isOnline()){
|
||||
//No Internet Connection
|
||||
showNoConnectionAlert()
|
||||
//Lets Stop the Incoming Request
|
||||
Response.Builder()
|
||||
.code(NoInternetErrorCode) // code(200.300) = successful else = unsuccessful
|
||||
.body("{}".toResponseBody(null)) // Whatever body
|
||||
.body("{}".toResponseBody(null)) // Empty Object
|
||||
.protocol(Protocol.HTTP_2)
|
||||
.message("No Internet Connection")
|
||||
.request(chain.request())
|
||||
@ -52,15 +53,7 @@ class NetworkInterceptor: Interceptor {
|
||||
.message(response.message)
|
||||
.request(chain.request())
|
||||
.build()
|
||||
// chain.proceed(chain.request())
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Converts REQUEST's Body to String
|
||||
* */
|
||||
private fun RequestBody?.bodyToString(): String {
|
||||
if (this == null) return ""
|
||||
val buffer = okio.Buffer()
|
||||
writeTo(buffer)
|
||||
return buffer.readUtf8()
|
||||
}
|
||||
}
|
@ -49,6 +49,11 @@ import javax.inject.Singleton
|
||||
@Module
|
||||
object Provider {
|
||||
|
||||
/*
|
||||
* mainActivity Instance to use whereEver Needed , as Its God Activity.
|
||||
* (i.e, Active Through out App' Lifecycle )
|
||||
* */
|
||||
|
||||
val mainActivity: MainActivity = MainActivity.getInstance()
|
||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
||||
|
||||
|
@ -29,9 +29,11 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.NavArgs
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
@ -53,7 +55,7 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
|
||||
super.onCreate(savedInstanceState)
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
mainActivity.onBackPressed()
|
||||
mainActivity.navController.popBackStack()
|
||||
}
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
}
|
||||
@ -64,11 +66,20 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = TrackListFragmentBinding.inflate(inflater,container,false)
|
||||
initializeAll()
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun initializeAll() {
|
||||
DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi
|
||||
DownloadHelper.sharedViewModel = sharedViewModel
|
||||
DownloadHelper.statusBar = binding.statusBar
|
||||
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.trackList.adapter = adapter
|
||||
initializeLiveDataObservers()
|
||||
}
|
||||
|
||||
@ -130,7 +141,6 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
|
||||
super.onPause()
|
||||
requireActivity().unregisterReceiver(updateUIReceiver)
|
||||
}
|
||||
|
||||
private fun checkIfAllDownloaded() {
|
||||
if(!viewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||
//All Tracks Downloaded
|
||||
@ -142,5 +152,5 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun applicationContext(): Context = requireActivity().applicationContext
|
||||
}
|
@ -116,19 +116,19 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/cover_image"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="22dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="#00000000"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:gravity="end"
|
||||
android:text='"Loading..."'
|
||||
android:textAlignment="viewEnd"
|
||||
android:textColor="#9AB3FF"
|
||||
android:textSize="28sp"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
@ -48,7 +48,7 @@
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
|
||||
android:textColor="#9AB3FF"
|
||||
android:textSize="20sp"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_download"
|
||||
app:layout_constraintStart_toStartOf="@+id/artist"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
Loading…
Reference in New Issue
Block a user