From 994b20d0a13dbd2518770683829cdea18b25e231 Mon Sep 17 00:00:00 2001 From: shabinder Date: Sun, 11 Jul 2021 02:20:31 +0530 Subject: [PATCH] Preferred Audio Quality Impl --- .../com/shabinder/spotiflyer/MainActivity.kt | 2 +- .../common/di/FetchPlatformQueryResult.kt | 24 ++++++++++++------- .../common/di/providers/ProvidersModule.kt | 2 +- .../common/di/providers/YoutubeMp3.kt | 3 ++- .../common/di/providers/YoutubeMusic.kt | 8 +++++-- .../requests/saavn/JioSaavnRequests.kt | 5 +++- .../providers/requests/youtubeMp3/Yt1sMp3.kt | 18 ++++++++++---- .../integration/SpotiFlyerPreferenceImpl.kt | 2 +- desktop/src/jvmMain/kotlin/Main.kt | 2 +- 9 files changed, 44 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 604d2d17..28269c65 100644 --- a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -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 ")) diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt index 2729639c..4442c41d 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/FetchPlatformQueryResult.kt @@ -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 = 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 { // 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( diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/ProvidersModule.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/ProvidersModule.kt index b4d46549..66734202 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/ProvidersModule.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/ProvidersModule.kt @@ -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()) } } \ No newline at end of file diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt index 5ba98a5c..5d674ec7 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMp3.kt @@ -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 = getLinkFromYt1sMp3(videoID).map { + suspend fun getMp3DownloadLink(videoID: String,quality: AudioQuality): SuspendableEvent = getLinkFromYt1sMp3(videoID,quality).map { corsApi + it } } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt index ac311a1b..5d660760 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/YoutubeMusic.kt @@ -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 { 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) diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/saavn/JioSaavnRequests.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/saavn/JioSaavnRequests.kt index b445a923..7b53bcae 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/saavn/JioSaavnRequests.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/saavn/JioSaavnRequests.kt @@ -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, + preferredQuality: AudioQuality ): SuspendableEvent = 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) diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/youtubeMp3/Yt1sMp3.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/youtubeMp3/Yt1sMp3.kt index db2b0d21..2fbf4465 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/youtubeMp3/Yt1sMp3.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/requests/youtubeMp3/Yt1sMp3.kt @@ -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 = getKey(videoID).flatMap { key -> + suspend fun getLinkFromYt1sMp3(videoID: String,quality: AudioQuality): SuspendableEvent = 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 = SuspendableEvent { + private suspend fun getKey(videoID: String,quality: AudioQuality): SuspendableEvent = 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 = SuspendableEvent { diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt index 10401e18..bd1f8d31 100644 --- a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt @@ -64,7 +64,7 @@ internal class SpotiFlyerPreferenceImpl( override fun selectNewDownloadDirectory() { actions.setDownloadDirectoryAction { - store.accept(Intent.SetDownloadDirectory(dir.defaultDir())) + store.accept(Intent.SetDownloadDirectory(it)) } } diff --git a/desktop/src/jvmMain/kotlin/Main.kt b/desktop/src/jvmMain/kotlin/Main.kt index f302f09d..a327d781 100644 --- a/desktop/src/jvmMain/kotlin/Main.kt +++ b/desktop/src/jvmMain/kotlin/Main.kt @@ -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} "))