mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 12:47:54 +01:00
Preferred Audio Quality Impl
This commit is contained in:
parent
a44f4cc061
commit
994b20d0a1
@ -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 "))
|
||||
|
@ -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(
|
||||
|
@ -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()) }
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -64,7 +64,7 @@ internal class SpotiFlyerPreferenceImpl(
|
||||
|
||||
override fun selectNewDownloadDirectory() {
|
||||
actions.setDownloadDirectoryAction {
|
||||
store.accept(Intent.SetDownloadDirectory(dir.defaultDir()))
|
||||
store.accept(Intent.SetDownloadDirectory(it))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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} "))
|
||||
|
Loading…
Reference in New Issue
Block a user