AudioQuality Parsing Improv

This commit is contained in:
shabinder 2021-09-02 23:51:11 +05:30
parent ba50bc789d
commit 86e6a9f3a8
12 changed files with 56 additions and 26 deletions

View File

@ -139,8 +139,8 @@ class ForegroundService : LifecycleService() {
lifecycleScope.launch { lifecycleScope.launch {
downloadService.value.executeSuspending { downloadService.value.executeSuspending {
fetcher.findBestDownloadLink(track).fold( fetcher.findBestDownloadLink(track).fold(
success = { url -> success = { res ->
enqueueDownload(url, track) enqueueDownload(res.first, track.apply { audioQuality = res.second })
}, },
failure = { error -> failure = { error ->
failed++ failed++

View File

@ -23,9 +23,9 @@ import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
object Versions { object Versions {
// App's Version (To be bumped at each update) // App's Version (To be bumped at each update)
const val versionName = "3.2.11" const val versionName = "3.3"
const val versionCode = 23 const val versionCode = 24
// Kotlin // Kotlin
const val kotlinVersion = "1.5.21" const val kotlinVersion = "1.5.21"

View File

@ -131,6 +131,7 @@ class AndroidFileManager(
val conversionResult = mediaConverter.convertAudioFile( val conversionResult = mediaConverter.convertAudioFile(
inputFilePath = songFile.absolutePath, inputFilePath = songFile.absolutePath,
outputFilePath = convertedFilePath, outputFilePath = convertedFilePath,
trackDetails.audioQuality
) )
conversionResult.map { outputFilePath -> conversionResult.map { outputFilePath ->

View File

@ -142,6 +142,7 @@ class DesktopFileManager(
val conversionResult = mediaConverter.convertAudioFile( val conversionResult = mediaConverter.convertAudioFile(
inputFilePath = songFile.absolutePath, inputFilePath = songFile.absolutePath,
outputFilePath = convertedFilePath, outputFilePath = convertedFilePath,
trackDetails.audioQuality
) )
conversionResult.map { outputFilePath -> conversionResult.map { outputFilePath ->

View File

@ -41,6 +41,7 @@ data class TrackDetails(
val progress: Int = 2, val progress: Int = 2,
val downloadLink: String? = null, val downloadLink: String? = null,
val downloaded: DownloadStatus = DownloadStatus.NotDownloaded, val downloaded: DownloadStatus = DownloadStatus.NotDownloaded,
var audioQuality: AudioQuality = AudioQuality.KBPS192,
var outputFilePath: String, // UriString in Android var outputFilePath: String, // UriString in Android
var videoID: String? = null, var videoID: String? = null,
) : Parcelable { ) : Parcelable {

View File

@ -1,5 +1,6 @@
package com.shabinder.common.models.saavn package com.shabinder.common.models.saavn
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -43,4 +44,6 @@ data class SaavnSong @OptIn(ExperimentalSerializationApi::class) constructor(
val vlink: String? = null, val vlink: String? = null,
val year: String, val year: String,
var downloaded: DownloadStatus = DownloadStatus.NotDownloaded var downloaded: DownloadStatus = DownloadStatus.NotDownloaded
) ) {
val audioQuality get() = if (is320Kbps) AudioQuality.KBPS320 else AudioQuality.KBPS160
}

View File

@ -91,27 +91,35 @@ class FetchPlatformQueryResult(
suspend fun findBestDownloadLink( suspend fun findBestDownloadLink(
track: TrackDetails, track: TrackDetails,
preferredQuality: AudioQuality = preferenceManager.audioQuality preferredQuality: AudioQuality = preferenceManager.audioQuality
): SuspendableEvent<String, Throwable> { ): SuspendableEvent<Pair<String, AudioQuality>, Throwable> {
var downloadLink: String? = null var downloadLink: String? = null
var audioQuality = AudioQuality.KBPS192
val errorTrace = buildString(track) { val errorTrace = buildString(track) {
if (track.videoID != null) { if (track.videoID != null) {
// We Already have VideoID // We Already have VideoID
downloadLink = when (track.source) { downloadLink = when (track.source) {
Source.JioSaavn -> { Source.JioSaavn -> {
saavnProvider.getSongFromID(track.videoID.requireNotNull()).component1()?.media_url saavnProvider.getSongFromID(track.videoID.requireNotNull()).component1()
?.also { audioQuality = it.audioQuality }
?.media_url
} }
Source.YouTube -> { Source.YouTube -> {
youtubeMp3.getMp3DownloadLink(track.videoID.requireNotNull(), preferredQuality) youtubeMp3.getMp3DownloadLink(
.let { ytMp3Link -> track.videoID.requireNotNull(),
if (ytMp3Link is SuspendableEvent.Failure || ytMp3Link.component1().isNullOrBlank()) { preferredQuality
).let { ytMp3Link ->
if (ytMp3Link is SuspendableEvent.Failure || ytMp3Link.component1()
.isNullOrBlank()
) {
appendPadded( appendPadded(
"Yt1sMp3 Failed for ${track.videoID}:", "Yt1sMp3 Failed for ${track.videoID}:",
ytMp3Link.component2() ytMp3Link.component2()
?: "couldn't fetch link for ${track.videoID} ,trying manual extraction" ?: "couldn't fetch link for ${track.videoID} ,trying manual extraction"
) )
appendLine("Trying Local Extraction") appendLine("Trying Local Extraction")
youtubeProvider.ytDownloader.getVideo(track.videoID!!).get()?.url youtubeProvider.ytDownloader.getVideo(track.videoID!!)
.get()?.url
} else ytMp3Link.component1() } else ytMp3Link.component1()
} }
} }
@ -137,24 +145,35 @@ class FetchPlatformQueryResult(
appendPadded("Fetching From Saavn Failed:", saavnError.stackTraceToString()) appendPadded("Fetching From Saavn Failed:", saavnError.stackTraceToString())
// Saavn Failed, Lets Try Fetching Now From Youtube Music // Saavn Failed, Lets Try Fetching Now From Youtube Music
youtubeMusic.findMp3SongDownloadURLYT(track, preferredQuality).also { youtubeMusic.findMp3SongDownloadURLYT(track, preferredQuality).also {
// Append Error To StackTrace
if (it is SuspendableEvent.Failure) if (it is SuspendableEvent.Failure)
appendPadded("Fetching From YT-Music Failed:", it.component2()?.stackTraceToString()) appendPadded(
"Fetching From YT-Music Failed:",
it.component2()?.stackTraceToString()
)
} }
} }
downloadLink = queryResult.component1() queryResult.component1()?.let {
downloadLink = it.first
audioQuality = it.second
}
} }
} }
return if (downloadLink.isNullOrBlank()) SuspendableEvent.error( return if (downloadLink.isNullOrBlank()) SuspendableEvent.error(
SpotiFlyerException.DownloadLinkFetchFailed(errorTrace) SpotiFlyerException.DownloadLinkFetchFailed(errorTrace)
) else SuspendableEvent.success(downloadLink.requireNotNull()) ) else SuspendableEvent.success(Pair(downloadLink.requireNotNull(),audioQuality))
} }
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
private fun addToDatabaseAsync(link: String, result: PlatformQueryResult) { private fun addToDatabaseAsync(link: String, result: PlatformQueryResult) {
GlobalScope.launch(dispatcherIO) { GlobalScope.launch(dispatcherIO) {
db?.add( db?.add(
result.folderType, result.title, link, result.coverUrl, result.trackList.size.toLong() result.folderType,
result.title,
link,
result.coverUrl,
result.trackList.size.toLong()
) )
} }
} }

View File

@ -3,10 +3,7 @@ package com.shabinder.common.providers.saavn
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.shabinder.common.core_components.file_manager.FileManager import com.shabinder.common.core_components.file_manager.FileManager
import com.shabinder.common.core_components.file_manager.finalOutputDir import com.shabinder.common.core_components.file_manager.finalOutputDir
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.*
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.event.coroutines.SuspendableEvent import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.saavn.SaavnSong import com.shabinder.common.models.saavn.SaavnSong
import com.shabinder.common.models.spotify.Source import com.shabinder.common.models.spotify.Source
@ -81,6 +78,7 @@ class SaavnProvider(
albumArtURL = it.image.replace("http:", "https:"), albumArtURL = it.image.replace("http:", "https:"),
lyrics = it.lyrics ?: it.lyrics_snippet, lyrics = it.lyrics ?: it.lyrics_snippet,
source = Source.JioSaavn, source = Source.JioSaavn,
audioQuality = if(it.is320Kbps) AudioQuality.KBPS320 else AudioQuality.KBPS160,
outputFilePath = fileManager.finalOutputDir(it.song, type, subFolder, fileManager.defaultDir() /*".m4a"*/) outputFilePath = fileManager.finalOutputDir(it.song, type, subFolder, fileManager.defaultDir() /*".m4a"*/)
) )
} }

View File

@ -32,18 +32,21 @@ interface JioSaavnRequests {
trackName: String, trackName: String,
trackArtists: List<String>, trackArtists: List<String>,
preferredQuality: AudioQuality preferredQuality: AudioQuality
): SuspendableEvent<String, Throwable> = searchForSong(trackName).map { songs -> ): SuspendableEvent<Pair<String,AudioQuality>, Throwable> = searchForSong(trackName).map { songs ->
val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull() ?: val bestMatch = sortByBestMatch(songs, trackName, trackArtists).keys.firstOrNull() ?:
throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName") throw SpotiFlyerException.DownloadLinkFetchFailed("No SAAVN Match Found for $trackName")
var audioQuality: AudioQuality = AudioQuality.KBPS160
val m4aLink: String by getSongFromID(bestMatch).map { song -> val m4aLink: String by getSongFromID(bestMatch).map { song ->
val optimalQuality = if (song.is320Kbps && ((preferredQuality.kbps.toIntOrNull() val optimalQuality = if (song.is320Kbps && ((preferredQuality.kbps.toIntOrNull()
?: 0) > 160) ?: 0) > 160)
) AudioQuality.KBPS320 else AudioQuality.KBPS160 ) AudioQuality.KBPS320 else AudioQuality.KBPS160
audioQuality = optimalQuality
song.media_url.requireNotNull().replaceAfterLast("_", "${optimalQuality.kbps}.mp4") song.media_url.requireNotNull().replaceAfterLast("_", "${optimalQuality.kbps}.mp4")
} }
m4aLink Pair(m4aLink,audioQuality)
} }
suspend fun searchForSong( suspend fun searchForSong(

View File

@ -50,7 +50,7 @@ class YoutubeMusic constructor(
suspend fun findMp3SongDownloadURLYT( suspend fun findMp3SongDownloadURLYT(
trackDetails: TrackDetails, trackDetails: TrackDetails,
preferredQuality: AudioQuality = fileManager.preferenceManager.audioQuality preferredQuality: AudioQuality = fileManager.preferenceManager.audioQuality
): SuspendableEvent<String, Throwable> { ): SuspendableEvent<Pair<String,AudioQuality>, Throwable> {
return getYTIDBestMatch(trackDetails).flatMap { videoID -> return getYTIDBestMatch(trackDetails).flatMap { videoID ->
// As YT compress Audio hence there is no benefit of quality for more than 192 // As YT compress Audio hence there is no benefit of quality for more than 192
val optimalQuality = val optimalQuality =
@ -67,6 +67,8 @@ class YoutubeMusic constructor(
message = "Caught Following Errors While Finding Downloadable Link for $videoID : \n${it.stackTraceToString()}" message = "Caught Following Errors While Finding Downloadable Link for $videoID : \n${it.stackTraceToString()}"
) )
} }
}.map {
Pair(it,optimalQuality)
} }
} }
} }

View File

@ -34,8 +34,9 @@ actual suspend fun downloadTracks(
list.forEach { trackDetails -> list.forEach { trackDetails ->
DownloadScope.executeSuspending { // Send Download to Pool. DownloadScope.executeSuspending { // Send Download to Pool.
fetcher.findBestDownloadLink(trackDetails).fold( fetcher.findBestDownloadLink(trackDetails).fold(
success = { url -> success = { res ->
downloadFile(url).collect { trackDetails.audioQuality = res.second
downloadFile(res.first).collect {
when (it) { when (it) {
is DownloadResult.Error -> { is DownloadResult.Error -> {
DownloadProgressFlow.emit( DownloadProgressFlow.emit(

View File

@ -33,8 +33,9 @@ actual suspend fun downloadTracks(
withContext(dispatcherIO) { withContext(dispatcherIO) {
allTracksStatus[track.title] = DownloadStatus.Queued allTracksStatus[track.title] = DownloadStatus.Queued
fetcher.findBestDownloadLink(track).fold( fetcher.findBestDownloadLink(track).fold(
success = { url -> success = { res ->
downloadFile(url).collect { track.audioQuality = res.second
downloadFile(res.first).collect {
when (it) { when (it) {
is DownloadResult.Success -> { is DownloadResult.Success -> {
println("Download Completed") println("Download Completed")