No More Web Scraping!,Speed Boosted& Less Crashes.

This commit is contained in:
Shabinder 2020-11-07 03:56:10 +05:30
parent 923e28617a
commit 099a103e98
11 changed files with 366 additions and 156 deletions

View File

@ -25,6 +25,7 @@
<w>spotifydownloader</w>
<w>spotifyler</w>
<w>thru</w>
<w>weyfdnx</w>
<w>youtu</w>
</words>
</dictionary>

View File

@ -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'

View File

@ -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()
}

View File

@ -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<YoutubeRequest>()
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<String>()
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<String>{
override fun onResponse(call: Call<String>, response: Response<String>) {
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<String>, 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
)

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<YoutubeTrack>()
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<JsonObject>("contents")
val resultBlocks = mutableListOf<JsonArray<JsonObject>>()
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<JsonObject>("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<JsonObject>("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<JsonObject>()
for(result in resultBlocks){
// Blindly gather available details
val availableDetails = mutableListOf<String>()
/*
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<JsonObject>("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()
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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

View File

@ -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
@ -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

View File

@ -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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<String>
}
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
}

View File

@ -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<Any?>
// val list = intent.getParcelableArrayListExtra<DownloadObject>("list") ?: intent.extras?.getParcelableArrayList<DownloadObject>("list")
// Log.i(tag,"Intent List Size: ${list!!.size}")
val obj = intent.getParcelableExtra<DownloadObject>("object") ?: intent.extras?.getParcelable<DownloadObject>("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()
}
}
/**

View File

@ -154,14 +154,5 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar_spotify" />
<WebView
android:id="@+id/webView_spotify"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_gravity="bottom"
android:visibility="gone"
app:layout_anchorGravity="bottom"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>