Preferred Audio Quality Impl

This commit is contained in:
shabinder 2021-07-11 02:20:31 +05:30
parent a44f4cc061
commit 994b20d0a1
9 changed files with 44 additions and 22 deletions

View File

@ -421,7 +421,7 @@ class MainActivity : ComponentActivity() {
if (f.canWrite()) {
// hell yeah :)
preferenceManager.setDownloadDirectory(path)
callBack(dir.defaultDir())
callBack(path)
showPopUpMessage(Strings.downloadDirectorySetTo("\n${dir.defaultDir()}"))
}else{
showPopUpMessage(Strings.noWriteAccess("\n$path "))

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import com.shabinder.common.database.DownloadRecordDatabaseQueries
import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.di.providers.GaanaProvider
import com.shabinder.common.di.providers.SaavnProvider
import com.shabinder.common.di.providers.SpotifyProvider
@ -26,6 +27,7 @@ import com.shabinder.common.di.providers.YoutubeMusic
import com.shabinder.common.di.providers.YoutubeProvider
import com.shabinder.common.di.providers.get
import com.shabinder.common.di.providers.requests.audioToMp3.AudioToMp3
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.TrackDetails
@ -48,6 +50,7 @@ class FetchPlatformQueryResult(
private val youtubeMp3: YoutubeMp3,
private val audioToMp3: AudioToMp3,
val dir: Dir,
val preferenceManager: PreferenceManager,
val logger: Kermit
) {
private val db: DownloadRecordDatabaseQueries?
@ -89,18 +92,19 @@ class FetchPlatformQueryResult(
// 1) Try Finding on JioSaavn (better quality upto 320KBPS)
// 2) If Not found try finding on Youtube Music
suspend fun findMp3DownloadLink(
track: TrackDetails
track: TrackDetails,
preferredQuality: AudioQuality = preferenceManager.audioQuality
): SuspendableEvent<String,Throwable> =
if (track.videoID != null) {
// We Already have VideoID
when (track.source) {
Source.JioSaavn -> {
saavnProvider.getSongFromID(track.videoID.requireNotNull()).flatMap { song ->
song.media_url?.let { audioToMp3.convertToMp3(it) } ?: findHighestQualityMp3Link(track)
song.media_url?.let { audioToMp3.convertToMp3(it) } ?: findMp3Link(track,preferredQuality)
}
}
Source.YouTube -> {
youtubeMp3.getMp3DownloadLink(track.videoID.requireNotNull()).flatMapError {
youtubeMp3.getMp3DownloadLink(track.videoID.requireNotNull(),preferredQuality).flatMapError {
logger.e("Yt1sMp3 Failed") { it.message ?: "couldn't fetch link for ${track.videoID} ,trying manual extraction" }
youtubeProvider.ytDownloader.getVideo(track.videoID!!).get()?.url?.let { m4aLink ->
audioToMp3.convertToMp3(m4aLink)
@ -109,24 +113,26 @@ class FetchPlatformQueryResult(
}
else -> {
/*We should never reach here for now*/
findHighestQualityMp3Link(track)
findMp3Link(track,preferredQuality)
}
}
} else {
findHighestQualityMp3Link(track)
findMp3Link(track,preferredQuality)
}
private suspend fun findHighestQualityMp3Link(
track: TrackDetails
private suspend fun findMp3Link(
track: TrackDetails,
preferredQuality: AudioQuality
):SuspendableEvent<String,Throwable> {
// Try Fetching Track from Jio Saavn
return saavnProvider.findMp3SongDownloadURL(
trackName = track.title,
trackArtists = track.artists
trackArtists = track.artists,
preferredQuality = preferredQuality
).flatMapError { saavnError ->
logger.e { "Fetching From Saavn Failed: \n${saavnError.stackTraceToString()}" }
// Saavn Failed, Lets Try Fetching Now From Youtube Music
youtubeMusic.findMp3SongDownloadURLYT(track).flatMapError { ytMusicError ->
youtubeMusic.findMp3SongDownloadURLYT(track,preferredQuality).flatMapError { ytMusicError ->
// If Both Failed Bubble the Exception Up with both StackTraces
SuspendableEvent.error(
SpotiFlyerException.DownloadLinkFetchFailed(

View File

@ -12,5 +12,5 @@ fun providersModule() = module {
single { YoutubeProvider(get(), get(), get()) }
single { YoutubeMp3(get(), get()) }
single { YoutubeMusic(get(), get(), get(), get(), get()) }
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
}

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di.providers
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.providers.requests.youtubeMp3.Yt1sMp3
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.corsApi
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.map
@ -37,7 +38,7 @@ interface YoutubeMp3: Yt1sMp3 {
}
}
suspend fun getMp3DownloadLink(videoID: String): SuspendableEvent<String,Throwable> = getLinkFromYt1sMp3(videoID).map {
suspend fun getMp3DownloadLink(videoID: String,quality: AudioQuality): SuspendableEvent<String,Throwable> = getLinkFromYt1sMp3(videoID,quality).map {
corsApi + it
}
}

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di.providers
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.providers.requests.audioToMp3.AudioToMp3
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.SpotiFlyerException
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.YoutubeTrack
@ -57,11 +58,14 @@ class YoutubeMusic constructor(
// Get Downloadable Link
suspend fun findMp3SongDownloadURLYT(
trackDetails: TrackDetails
trackDetails: TrackDetails,
preferredQuality: AudioQuality
): SuspendableEvent<String, Throwable> {
return getYTIDBestMatch(trackDetails).flatMap { videoID ->
// As YT compress Audio hence there is no benefit of quality for more than 192
val optimalQuality = if((preferredQuality.kbps.toIntOrNull() ?: 0) > 192) AudioQuality.KBPS192 else preferredQuality
// 1 Try getting Link from Yt1s
youtubeMp3.getMp3DownloadLink(videoID).flatMapError {
youtubeMp3.getMp3DownloadLink(videoID, optimalQuality).flatMapError {
// 2 if Yt1s failed , Extract Manually
youtubeProvider.ytDownloader.getVideo(videoID).get()?.url?.let { m4aLink ->
audioToMp3.convertToMp3(m4aLink)

View File

@ -3,6 +3,7 @@ package com.shabinder.common.di.providers.requests.saavn
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.globalJson
import com.shabinder.common.di.providers.requests.audioToMp3.AudioToMp3
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.corsApi
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.map
@ -38,11 +39,13 @@ interface JioSaavnRequests {
suspend fun findMp3SongDownloadURL(
trackName: String,
trackArtists: List<String>,
preferredQuality: AudioQuality
): SuspendableEvent<String,Throwable> = searchForSong(trackName).map { songs ->
val bestMatches = sortByBestMatch(songs, trackName, trackArtists)
val m4aLink: String by getSongFromID(bestMatches.keys.first()).map { song ->
song.media_url.requireNotNull()
val optimalQuality = if(song.is320Kbps && ((preferredQuality.kbps.toIntOrNull() ?: 0) > 160)) AudioQuality.KBPS320 else AudioQuality.KBPS160
song.media_url.requireNotNull().replaceAfterLast("_","${optimalQuality.kbps}.mp4")
}
val mp3Link by audioToMp3.convertToMp3(m4aLink)

View File

@ -17,6 +17,7 @@
package com.shabinder.common.di.providers.requests.youtubeMp3
import co.touchlab.kermit.Kermit
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.corsApi
import com.shabinder.common.models.event.coroutines.SuspendableEvent
import com.shabinder.common.models.event.coroutines.flatMap
@ -42,7 +43,7 @@ interface Yt1sMp3 {
/*
* Downloadable Mp3 Link for YT videoID.
* */
suspend fun getLinkFromYt1sMp3(videoID: String): SuspendableEvent<String,Throwable> = getKey(videoID).flatMap { key ->
suspend fun getLinkFromYt1sMp3(videoID: String,quality: AudioQuality): SuspendableEvent<String,Throwable> = getKey(videoID,quality).flatMap { key ->
getConvertedMp3Link(videoID, key).map {
it["dlink"].requireNotNull()
.jsonPrimitive.content.replace("\"", "")
@ -53,7 +54,7 @@ interface Yt1sMp3 {
* POST:https://yt1s.com/api/ajaxSearch/index
* Body Form= q:yt video link ,vt:format=mp3
* */
private suspend fun getKey(videoID: String): SuspendableEvent<String,Throwable> = SuspendableEvent {
private suspend fun getKey(videoID: String,quality: AudioQuality): SuspendableEvent<String,Throwable> = SuspendableEvent {
val response: JsonObject = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index") {
body = FormDataContent(
Parameters.build {
@ -63,10 +64,17 @@ interface Yt1sMp3 {
)
}
response.getJsonObject("links")
val mp3Keys = response.getJsonObject("links")
.getJsonObject("mp3")
.getJsonObject("192")
?.get("k").requireNotNull().jsonPrimitive.content
val requestedKBPS = when(quality) {
AudioQuality.KBPS128 -> "mp3128"
else -> quality.kbps
}
val specificQualityKey = mp3Keys.getJsonObject(requestedKBPS) ?: mp3Keys.getJsonObject("192")
specificQualityKey?.get("k").requireNotNull().jsonPrimitive.content
}
private suspend fun getConvertedMp3Link(videoID: String, key: String): SuspendableEvent<JsonObject,Throwable> = SuspendableEvent {

View File

@ -64,7 +64,7 @@ internal class SpotiFlyerPreferenceImpl(
override fun selectNewDownloadDirectory() {
actions.setDownloadDirectoryAction {
store.accept(Intent.SetDownloadDirectory(dir.defaultDir()))
store.accept(Intent.SetDownloadDirectory(it))
}
}

View File

@ -116,7 +116,7 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
val directory = fileChooser.selectedFile
if(directory.canWrite()){
preferenceManager.setDownloadDirectory(directory.absolutePath)
callBack(dir.defaultDir())
callBack(directory.absolutePath)
showPopUpMessage("${Strings.setDownloadDirectory()} \n${dir.defaultDir()}")
} else {
showPopUpMessage(Strings.noWriteAccess("\n${directory.absolutePath} "))