diff --git a/.idea/dictionaries/shabinder.xml b/.idea/dictionaries/shabinder.xml
index 7218236f..726356e5 100755
--- a/.idea/dictionaries/shabinder.xml
+++ b/.idea/dictionaries/shabinder.xml
@@ -25,6 +25,7 @@
spotifydownloader
spotifyler
thru
+ weyfdnx
youtu
diff --git a/app/build.gradle b/app/build.gradle
index d390587f..36a5fdfe 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -115,10 +115,13 @@ dependencies {
implementation 'com.squareup.moshi:moshi:1.11.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
+ implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
+ implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+ implementation 'com.beust:klaxon:5.4'
implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'com.shreyaspatil:EasyUpiPayment:3.0.0'
- implementation 'com.github.sealedtx:java-youtube-downloader:2.4.2'
+ implementation 'com.github.sealedtx:java-youtube-downloader:2.4.3'
implementation "androidx.tonyodev.fetch2:xfetch2:3.1.5"
implementation 'com.github.javiersantos:AppUpdater:2.7'
diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
index b4a1272e..4868a89b 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
@@ -69,9 +69,6 @@ class MainActivity : AppCompatActivity(){
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
- //starting Notification and Downloader Service!
- SpotifyDownloadHelper.startService(this)
-
if(sharedViewModel.spotifyService.value == null){
authenticateSpotify()
}else{
@@ -86,6 +83,9 @@ class MainActivity : AppCompatActivity(){
sharedViewModel.isConnected.value = isConnected
Log.i("Connection Status", isConnected.toString())
+ //starting Notification and Downloader Service!
+ SpotifyDownloadHelper.startService(this)
+
handleIntentFromExternalActivity()
}
diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt
index a68427d6..b783a9a5 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt
@@ -17,18 +17,13 @@
package com.shabinder.spotiflyer.downloadHelper
-import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Environment
-import android.os.Handler
import android.util.Log
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
-import android.webkit.WebView
-import android.webkit.WebViewClient
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.github.kiulian.downloader.YoutubeDownloader
@@ -37,25 +32,27 @@ import com.github.kiulian.downloader.model.quality.AudioQuality
import com.shabinder.spotiflyer.models.DownloadObject
import com.shabinder.spotiflyer.models.Track
import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel
+import com.shabinder.spotiflyer.utils.YoutubeMusicApi
import com.shabinder.spotiflyer.utils.getEmojiByUnicode
+import com.shabinder.spotiflyer.utils.makeJsonBody
import com.shabinder.spotiflyer.worker.ForegroundService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
import java.io.File
object SpotifyDownloadHelper {
- var webView:WebView? = null
var context : Context? = null
var statusBar:TextView? = null
+ var youtubeMusicApi:YoutubeMusicApi? = null
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
var spotifyViewModel: SpotifyViewModel? = null
- private var isBrowserLoading = false
- private var total = 0
- private var Processed = 0
- private var notFound = 0
- private var listProcessed:Boolean = false
- var youtubeList = mutableListOf()
+ var total = 0
+ var Processed = 0
+ var notFound = 0
/**
* Function To Download All Tracks Available in a List
@@ -70,16 +67,9 @@ object SpotifyDownloadHelper {
if(it.downloaded == "Downloaded"){//Download Already Present!!
Processed++
}else{
- if(isBrowserLoading){//WebView Busy!!
- if (listProcessed){//Previous List request progress check
- getYTLink(type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it)
- listProcessed = false//Notifying A list Processing Started
- }else{//Adding Requests to a Queue
- youtubeList.add(YoutubeRequest(type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it))
- }
- }else{
- getYTLink(type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it)
- }
+ val artistsList = mutableListOf()
+ it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
+ searchYTMusic(type,subFolder,ytDownloader,"${it.name} - ${artistsList.joinToString(",")}", it)
}
updateStatusBar()
}
@@ -88,66 +78,32 @@ object SpotifyDownloadHelper {
}
-
- //TODO CleanUp here and there!!
- @SuppressLint("SetJavaScriptEnabled")
- suspend fun getYTLink(type:String,
- subFolder:String?,
- ytDownloader: YoutubeDownloader?,
- searchQuery: String,
- track: Track){
- isBrowserLoading = true // Notify Web View Started Loading
- val searchText = searchQuery.replace("\\s".toRegex(), "+")
- val url = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=$searchText"
- Log.i("DH YT LINK ",url)
- applyWebViewSettings(webView!!)
- withContext(Dispatchers.Main){
- webView!!.loadUrl(url)
- webView!!.webViewClient = object : WebViewClient() {
- override fun onPageFinished(view: WebView?, url: String?) {
- super.onPageFinished(view, url)
- view?.evaluateJavascript(
- "document.getElementsByClassName(\"yt-simple-endpoint style-scope ytd-video-renderer\")[0].href"
- ) { value ->
- Log.i("YT-id Link", value.toString().replace("\"", ""))
- val id = value!!.substringAfterLast("=", "error").replace("\"", "")
- Log.i("YT-ID", id)
- if (id != "error") {//Link extracting error
- Processed++
- downloadFile(subFolder, type, track, ytDownloader, id)
- }else notFound++
- updateStatusBar()
- if (youtubeList.isNotEmpty()) {
- val request = youtubeList[0]
- spotifyViewModel!!.uiScope.launch {
- getYTLink(
- request.type,
- request.subFolder,
- request.ytDownloader,
- request.searchQuery,
- request.track
- )
- }
- youtubeList.remove(request)
- if (youtubeList.size == 0) {//list processing completed , webView is free again!
- isBrowserLoading = false
- listProcessed = true
- }
- } else {//YT List Empty....Maybe it was one Single Download
- Handler().postDelayed({//Delay of 1.5 sec
- if (youtubeList.isEmpty()) {//Lets Make It sure , There are No more Downloads In Queue.....
- isBrowserLoading = false
- listProcessed = true
- }
- }, 1500)
- }
+ suspend fun searchYTMusic(type:String,
+ subFolder:String?,
+ ytDownloader: YoutubeDownloader?,
+ searchQuery: String,
+ track: Track){
+ val jsonBody = makeJsonBody(searchQuery.trim())
+ youtubeMusicApi?.getYoutubeMusicResponse(jsonBody)?.enqueue(
+ object : Callback{
+ override fun onResponse(call: Call, response: Response) {
+ spotifyViewModel?.uiScope?.launch {
+ Log.i("YT API BODY",response.body().toString())
+ Log.i("YT Search Query",searchQuery)
+ getYTLink(type,subFolder,ytDownloader,response.body().toString(),track)
}
}
+
+ override fun onFailure(call: Call, t: Throwable) {
+ Log.i("YT API Fail",t.message.toString())
+ }
}
- }
+ )
+
}
- private fun updateStatusBar() {
+
+ fun updateStatusBar() {
statusBar!!.visibility = View.VISIBLE
statusBar?.text = "Total: $total ${getEmojiByUnicode(0x2705)}: $Processed ${getEmojiByUnicode(0x274C)}: $notFound"
}
@@ -158,7 +114,6 @@ object SpotifyDownloadHelper {
withContext(Dispatchers.IO) {
try {
val video = ytDownloader?.getVideo(id)
- val detail = video?.details()
val format: Format? = try {
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
} catch (e: java.lang.IndexOutOfBoundsException) {
@@ -191,18 +146,21 @@ object SpotifyDownloadHelper {
)
Log.i("DH", outputFile)
startService(context!!, downloadObject)
+ Processed++
+ spotifyViewModel?.uiScope?.launch(Dispatchers.Main) {
+ updateStatusBar()
+ }
}
}catch (e: com.github.kiulian.downloader.YoutubeException){
- Log.i("DH", "Error- Maybe Network")
+ Log.i("DH", e.message)
}
}
}
}
-
fun startService(context:Context,obj:DownloadObject? = null ) {
val serviceIntent = Intent(context, ForegroundService::class.java)
- serviceIntent.putExtra("object",obj)
+ obj?.let { serviceIntent.putExtra("object",it) }
ContextCompat.startForegroundService(context, serviceIntent)
}
@@ -255,35 +213,4 @@ object SpotifyDownloadHelper {
anim.repeatCount = Animation.INFINITE
statusBar?.animation = anim
}
- @SuppressLint("SetJavaScriptEnabled")
- fun applyWebViewSettings(webView: WebView) {
- val desktopUserAgent =
- "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0"
- val mobileUserAgent =
- "Mozilla/5.0 (Linux; U; Android 4.4; en-us; Nexus 4 Build/JOP24G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
-
- //Choose Mobile/Desktop client.
- webView.settings.userAgentString = desktopUserAgent
- webView.settings.loadWithOverviewMode = true
- webView.settings.builtInZoomControls = true
- webView.settings.setSupportZoom(true)
- webView.isScrollbarFadingEnabled = false
- webView.scrollBarStyle = WebView.SCROLLBARS_OUTSIDE_OVERLAY
- webView.settings.displayZoomControls = false
- webView.settings.useWideViewPort = true
- webView.settings.javaScriptEnabled = true
- webView.settings.loadsImagesAutomatically = false
- webView.settings.blockNetworkImage = true
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- webView.settings.safeBrowsingEnabled = true
- }
- }
-}
-data class YoutubeRequest(
- val type:String,
- val subFolder:String?,
- val ytDownloader: YoutubeDownloader?,
- val searchQuery: String,
- val track: Track,
- val index: Int? = null
-)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt
new file mode 100644
index 00000000..daa90974
--- /dev/null
+++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt
@@ -0,0 +1,190 @@
+/*
+ * 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 .
+ */
+
+package com.shabinder.spotiflyer.downloadHelper
+
+import android.util.Log
+import com.beust.klaxon.JsonArray
+import com.beust.klaxon.JsonObject
+import com.beust.klaxon.Parser
+import com.github.kiulian.downloader.YoutubeDownloader
+import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.downloadFile
+import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.notFound
+import com.shabinder.spotiflyer.models.Track
+import com.shabinder.spotiflyer.models.YoutubeTrack
+
+/*
+* Thanks and credits To https://github.com/spotDL/spotify-downloader
+* */
+fun getYTLink(type:String,
+ subFolder:String?,
+ ytDownloader: YoutubeDownloader?,
+ response: String,
+ track: Track
+){
+ //TODO Download File
+ val youtubeTracks = mutableListOf()
+ val parser: Parser = Parser.default()
+ val stringBuilder: StringBuilder = StringBuilder(response)
+ val responseObj: JsonObject = parser.parse(stringBuilder) as JsonObject
+ val contentBlocks = responseObj.obj("contents")?.obj("sectionListRenderer")?.array("contents")
+ val resultBlocks = mutableListOf>()
+ if (contentBlocks != null) {
+ Log.i("Total Content Blocks:", contentBlocks.size.toString())
+ for (cBlock in contentBlocks){
+ /**
+ *Ignore user-suggestion
+ *The 'itemSectionRenderer' field is for user notices (stuff like - 'showing
+ *results for xyz, search for abc instead') we have no use for them, the for
+ *loop below if throw a keyError if we don't ignore them
+ */
+ if(cBlock.containsKey("itemSectionRenderer")){
+ continue
+ }
+
+ for(contents in cBlock.obj("musicShelfRenderer")?.array("contents") ?: listOf()){
+ /**
+ * apparently content Blocks without an 'overlay' field don't have linkBlocks
+ * I have no clue what they are and why there even exist
+ *
+ if(!contents.containsKey("overlay")){
+ println(contents)
+ continue
+ TODO check and correct
+ }*/
+
+ val result = contents.obj("musicResponsiveListItemRenderer")
+ ?.array("flexColumns")
+
+ //Add the linkBlock
+ val linkBlock = contents.obj("musicResponsiveListItemRenderer")
+ ?.obj("overlay")
+ ?.obj("musicItemThumbnailOverlayRenderer")
+ ?.obj("content")
+ ?.obj("musicPlayButtonRenderer")
+ ?.obj("playNavigationEndpoint")
+
+ // detailsBlock is always a list, so we just append the linkBlock to it
+ // instead of carrying along all the other junk from "musicResponsiveListItemRenderer"
+ linkBlock?.let { result?.add(it) }
+ result?.let { resultBlocks.add(it) }
+ }
+ }
+
+ /* We only need results that are Songs or Videos, so we filter out the rest, since
+ ! Songs and Videos are supplied with different details, extracting all details from
+ ! both is just carrying on redundant data, so we also have to selectively extract
+ ! relevant details. What you need to know to understand how we do that here:
+ !
+ ! Songs details are ALWAYS in the following order:
+ ! 0 - Name
+ ! 1 - Type (Song)
+ ! 2 - Artist
+ ! 3 - Album
+ ! 4 - Duration (mm:ss)
+ !
+ ! Video details are ALWAYS in the following order:
+ ! 0 - Name
+ ! 1 - Type (Video)
+ ! 2 - Channel
+ ! 3 - Viewers
+ ! 4 - Duration (hh:mm:ss)
+ !
+ ! We blindly gather all the details we get our hands on, then
+ ! cherrypick the details we need based on their index numbers,
+ ! we do so only if their Type is 'Song' or 'Video
+ */
+
+ val simplifiedResults = mutableListOf()
+
+ for(result in resultBlocks){
+
+ // Blindly gather available details
+ val availableDetails = mutableListOf()
+
+ /*
+ Filter Out dummies here itself
+ ! 'musicResponsiveListItemFlexColumnRenderer' should have more that one
+ ! sub-block, if not its a dummy, why does the YTM response contain dummies?
+ ! I have no clue. We skip these.
+
+ ! Remember that we appended the linkBlock to result, treating that like the
+ ! other constituents of a result block will lead to errors, hence the 'in
+ ! result[:-1] ,i.e., skip last element in array '
+ */
+ for(detail in result.subList(0,result.size-2)){
+ if(detail.obj("musicResponsiveListItemFlexColumnRenderer")?.size!! < 2) continue
+
+ // if not a dummy, collect All Variables
+ detail.obj("musicResponsiveListItemFlexColumnRenderer")
+ ?.obj("text")
+ ?.array("runs")?.get(0)?.get("text")?.let {
+ availableDetails.add(
+ it.toString()
+ )
+ }
+ }
+
+ /*
+ ! Filter Out non-Song/Video results and incomplete results here itself
+ ! From what we know about detail order, note that [1] - indicate result type
+ */
+ if ( availableDetails.size > 1 && availableDetails[1] in listOf("Song","Video") ){
+
+ // skip if result is in hours instead of minutes (no song is that long)
+// if(availableDetails[4].split(':').size != 2) continue TODO
+
+ /*
+ ! grab position of result
+ ! This helps for those oddball cases where 2+ results are rated equally,
+ ! lower position --> better match
+ */
+ val resultPosition = resultBlocks.indexOf(result)
+
+ /*
+ ! grab Video ID
+ ! this is nested as [playlistEndpoint/watchEndpoint][videoId/playlistId/...]
+ ! so hardcoding the dict keys for data look up is an ardours process, since
+ ! the sub-block pattern is fixed even though the key isn't, we just
+ ! reference the dict keys by index
+ */
+
+ val videoId:String = result.last().obj("watchEndpoint")?.get("videoId") as String
+ val ytTrack = YoutubeTrack(
+ name = availableDetails[0],
+ type = availableDetails[1],
+ artist = availableDetails[2],
+ videoId = videoId
+ )
+ youtubeTracks.add(ytTrack)
+ }
+ }
+ }
+ //Songs First, Videos Later
+ youtubeTracks.sortWith { o1: YoutubeTrack, o2: YoutubeTrack -> o1.type.toString().compareTo(o2.type.toString()) }
+
+ if(youtubeTracks.firstOrNull()?.videoId.isNullOrBlank()) notFound++
+ else downloadFile(
+ subFolder,
+ type,
+ track,
+ ytDownloader,
+ id = youtubeTracks[0].videoId.toString()
+ )
+ Log.i("DHelper YT ID", youtubeTracks.firstOrNull()?.videoId ?: "Not Found")
+ SpotifyDownloadHelper.updateStatusBar()
+}
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt
new file mode 100644
index 00000000..4ea7de9d
--- /dev/null
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 .
+ */
+
+package com.shabinder.spotiflyer.models
+
+import android.os.Parcelable
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class YoutubeTrack(
+ var name: String? = null,
+ var type: String? = null, // Song / Video
+ var artist: String? = null,
+ var videoId: String? = null
+):Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt
index 9e2835b3..b4a2d6e5 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt
@@ -29,7 +29,6 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.webkit.WebView
import android.widget.Toast
import androidx.core.net.toUri
import androidx.databinding.DataBindingUtil
@@ -50,6 +49,7 @@ import com.shabinder.spotiflyer.databinding.SpotifyFragmentBinding
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
import com.shabinder.spotiflyer.models.Track
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
+import com.shabinder.spotiflyer.utils.YoutubeMusicApi
import com.shabinder.spotiflyer.utils.bindImage
import com.shabinder.spotiflyer.utils.copyTo
import com.shabinder.spotiflyer.utils.rotateAnim
@@ -70,7 +70,7 @@ class SpotifyFragment : Fragment() {
private lateinit var sharedViewModel: SharedViewModel
private lateinit var adapterSpotify:SpotifyTrackListAdapter
@Inject lateinit var ytDownloader:YoutubeDownloader
- private var webView: WebView? = null
+ @Inject lateinit var youtubeMusicApi: YoutubeMusicApi
private var intentFilter:IntentFilter? = null
private var updateUIReceiver: BroadcastReceiver? = null
@@ -88,7 +88,7 @@ class SpotifyFragment : Fragment() {
val args = SpotifyFragmentArgs.fromBundle(requireArguments())
val spotifyLink = args.link
-
+
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
@@ -231,14 +231,13 @@ class SpotifyFragment : Fragment() {
* Basic Initialization
**/
private fun initializeAll() {
- webView = binding.webViewSpotify
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
spotifyViewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
spotifyViewModel.spotifyService = it
})
- SpotifyDownloadHelper.webView = binding.webViewSpotify
SpotifyDownloadHelper.context = requireContext()
+ SpotifyDownloadHelper.youtubeMusicApi = youtubeMusicApi
SpotifyDownloadHelper.spotifyViewModel = spotifyViewModel
SpotifyDownloadHelper.statusBar = binding.StatusBarSpotify
binding.trackListSpotify.adapter = adapterSpotify
diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt
index 57726455..3638edf0 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt
@@ -35,7 +35,9 @@ import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.converter.scalars.ScalarsConverterFactory
import javax.inject.Singleton
@InstallIn(ApplicationComponent::class)
@@ -97,4 +99,17 @@ object Provider {
return retrofit.create(SpotifyServiceTokenRequest::class.java)
}
+ @Provides
+ @Singleton
+ fun getYoutubeMusicApi():YoutubeMusicApi{
+
+ val retrofit = Retrofit.Builder()
+ .baseUrl("https://music.youtube.com/youtubei/v1/")
+ .addConverterFactory(ScalarsConverterFactory.create())
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+
+ return retrofit.create(YoutubeMusicApi::class.java)
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt
new file mode 100644
index 00000000..ccd72c67
--- /dev/null
+++ b/app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt
@@ -0,0 +1,58 @@
+/*
+ * 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 .
+ */
+
+package com.shabinder.spotiflyer.utils
+
+import com.beust.klaxon.JsonObject
+import retrofit2.Call
+import retrofit2.http.Body
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+
+const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
+/*val body = """{
+ "context": {
+ "client": {
+ "clientName": "WEB_REMIX",
+ "clientVersion": "0.1"
+ }
+ },
+ "query": "songSearchQuery"
+}"""*/
+interface YoutubeMusicApi {
+
+ @Headers("Content-Type: application/json", "Referer: https://music.youtube.com/search")
+ @POST("search?alt=json&key=$apiKey")
+ fun getYoutubeMusicResponse(@Body text: JsonObject): Call
+
+}
+
+fun makeJsonBody(query: String):JsonObject{
+ val client = JsonObject()
+ client["clientName"] = "WEB_REMIX"
+ client["clientVersion"] = "0.1"
+
+ val context = JsonObject()
+ context["client"] = client
+
+ val mainObject = JsonObject()
+ mainObject["context"] = context
+ mainObject["query"] = query
+
+ return mainObject
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt
index a1922fa2..ba103032 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt
@@ -17,6 +17,7 @@
package com.shabinder.spotiflyer.worker
+import android.annotation.SuppressLint
import android.app.*
import android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
import android.content.BroadcastReceiver
@@ -144,33 +145,29 @@ class ForegroundService : Service(){
return channelId
}
+ @SuppressLint("WakelockTimeout")
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
// Send a notification that service is started
Log.i(tag,"Service Started.")
startForeground()
- //do heavy work on a background thread
- //val list = intent.getSerializableExtra("list") as List
-// val list = intent.getParcelableArrayListExtra("list") ?: intent.extras?.getParcelableArrayList("list")
-// Log.i(tag,"Intent List Size: ${list!!.size}")
- val obj = intent.getParcelableExtra("object") ?: intent.extras?.getParcelable("object")
+ val obj:DownloadObject? = intent.getParcelableExtra("object") ?: intent.extras?.getParcelable("object")
obj?.let {
total ++
-// Log.i(tag,"Intent List Size: ${list!!.size}")
updateNotification()
serviceScope.launch {
- val request= Request(obj.url, obj.outputDir)
- request.priority = Priority.NORMAL
- request.networkType = NetworkType.ALL
+ val request= Request(obj.url, obj.outputDir)
+ request.priority = Priority.NORMAL
+ request.networkType = NetworkType.ALL
- fetch!!.enqueue(request,
- {
- obj.track?.let { it1 -> requestMap.put(it, it1) }
- downloadList.remove(obj)
- Log.i(tag, "Enqueuing Download")
- },
- {
- Log.i(tag, "Enqueuing Error:${it.throwable.toString()}")}
- )
+ fetch!!.enqueue(request,
+ {
+ obj.track?.let { it1 -> requestMap.put(it, it1) }
+ downloadList.remove(obj)
+ Log.i(tag, "Enqueuing Download")
+ },
+ {
+ Log.i(tag, "Enqueuing Error:${it.throwable.toString()}")}
+ )
}
}
@@ -316,12 +313,6 @@ class ForegroundService : Service(){
messageList[messageList.indexOf(message)] = ""
}
}
- //Notify Download Completed
- val intent = Intent()
- .setAction("track_download_completed")
- .putExtra("track",track)
- this@ForegroundService.sendBroadcast(intent)
-
serviceScope.launch {
try{
@@ -457,11 +448,17 @@ class ForegroundService : Service(){
newFile.renameTo(file)
converted++
updateNotification()
+
+ //Notify Download Completed
+ val intent = Intent()
+ .setAction("track_download_completed")
+ .putExtra("track",track)
+ this@ForegroundService.sendBroadcast(intent)
+
//All tasks completed (REST IN PEACE)
if(converted == total){
onDestroy()
}
-
}
/**
diff --git a/app/src/main/res/layout/spotify_fragment.xml b/app/src/main/res/layout/spotify_fragment.xml
index 2270feaf..6b363de5 100755
--- a/app/src/main/res/layout/spotify_fragment.xml
+++ b/app/src/main/res/layout/spotify_fragment.xml
@@ -154,14 +154,5 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar_spotify" />
-
-
-