Skip to content

Commit

Permalink
98% Local Playlist. More change to fixed no internet connection
Browse files Browse the repository at this point in the history
  • Loading branch information
maxrave-dev committed Nov 6, 2024
1 parent a388906 commit 56f891c
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 66 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ fun HomeItem(
NowPlayingBottomSheet(
isBottomSheetVisible = bottomSheetShow,
onDismiss = { bottomSheetShow = false },
songEntity = songEntity,
song = songEntity,
navController = navController,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fun NowPlayingBottomSheet(
isBottomSheetVisible: Boolean,
onDismiss: () -> Unit,
navController: NavController,
songEntity: SongEntity?,
song: SongEntity?,
viewModel: NowPlayingBottomSheetViewModel = koinViewModel(),
setSleepTimerEnable: Boolean = false,
changeMainLyricsProviderEnable: Boolean = false,
Expand Down Expand Up @@ -133,8 +133,8 @@ fun NowPlayingBottomSheet(
mutableStateOf(false)
}

LaunchedEffect(key1 = songEntity) {
viewModel.setSongEntity(songEntity)
LaunchedEffect(key1 = song) {
viewModel.setSongEntity(song)
}

if (addToAPlaylist) {
Expand Down Expand Up @@ -275,7 +275,6 @@ fun NowPlayingBottomSheet(
}

if (isBottomSheetVisible) {
val songEntity = uiState.songEntity ?: return
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = modelBottomSheetState,
Expand Down Expand Up @@ -317,7 +316,7 @@ fun NowPlayingBottomSheet(
verticalAlignment = Alignment.CenterVertically,
) {
CoilImage(
imageModel = { songEntity.thumbnails },
imageModel = { uiState.songEntity?.thumbnails },
imageOptions =
ImageOptions(
contentScale = ContentScale.Inside,
Expand All @@ -340,7 +339,7 @@ fun NowPlayingBottomSheet(
verticalArrangement = Arrangement.Center,
) {
Text(
text = songEntity.title,
text = uiState.songEntity?.title ?: "",
style = typo.labelMedium,
maxLines = 1,
modifier =
Expand All @@ -352,7 +351,7 @@ fun NowPlayingBottomSheet(
.focusable(),
)
Text(
text = songEntity.artistName?.connectArtists() ?: "",
text = uiState.songEntity?.artistName?.connectArtists() ?: "",
style = typo.bodyMedium,
maxLines = 1,
modifier =
Expand Down Expand Up @@ -386,14 +385,14 @@ fun NowPlayingBottomSheet(
}
}
CheckBoxActionButton(
defaultChecked = songEntity.liked,
defaultChecked = uiState.songEntity?.liked ?: false,
onChangeListener = {
viewModel.onUIEvent(NowPlayingBottomSheetUIEvent.ToggleLike)
},
)
ActionButton(
icon =
when (songEntity.downloadState) {
when (uiState.songEntity?.downloadState) {
DownloadState.STATE_NOT_DOWNLOADED ->
painterResource(
R.drawable.outline_download_for_offline_24,
Expand All @@ -420,7 +419,7 @@ fun NowPlayingBottomSheet(
)
},
text =
when (songEntity.downloadState) {
when (uiState.songEntity?.downloadState) {
DownloadState.STATE_NOT_DOWNLOADED -> R.string.download
DownloadState.STATE_DOWNLOADING -> R.string.downloading
DownloadState.STATE_DOWNLOADED -> R.string.downloaded
Expand Down Expand Up @@ -456,14 +455,14 @@ fun NowPlayingBottomSheet(
}
ActionButton(
icon = painterResource(id = R.drawable.baseline_album_24),
text = if (songEntity.albumName.isNullOrBlank()) R.string.no_album else null,
textString = songEntity.albumName,
enable = !songEntity.albumName.isNullOrBlank(),
text = if (uiState.songEntity?.albumName.isNullOrBlank()) R.string.no_album else null,
textString = uiState.songEntity?.albumName,
enable = !uiState.songEntity?.albumName.isNullOrBlank(),
) {
navController.navigateSafe(
R.id.action_global_albumFragment,
Bundle().apply {
putString("browseId", songEntity.albumId)
putString("browseId", uiState.songEntity?.albumId)
},
)
}
Expand All @@ -472,10 +471,10 @@ fun NowPlayingBottomSheet(
text = R.string.start_radio,
) {
val args = Bundle()
args.putString("radioId", "RDAMVM${songEntity.videoId}")
args.putString("radioId", "RDAMVM${uiState.songEntity?.videoId}")
args.putString(
"videoId",
songEntity.videoId,
uiState.songEntity?.videoId,
)
hideModalBottomSheet()
navController.navigateSafe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -793,14 +793,17 @@ fun PlaylistScreen(
EndOfPage()
}
}
if (itemBottomSheetShow) {
if (itemBottomSheetShow && currentItem != null) {
val track = currentItem ?: return
NowPlayingBottomSheet(
isBottomSheetVisible = itemBottomSheetShow,
isBottomSheetVisible = true,
onDelete = { viewModel.deleteItem(uiState.id, track) },
onDismiss = { itemBottomSheetShow = false },
onDismiss = {
itemBottomSheetShow = false
currentItem = null
},
navController = navController,
songEntity = track,
song = track,
)
}
if (playlistBottomSheetShow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ fun NowPlayingScreen(
showSheet = false
},
navController = navController,
songEntity = null, // Auto set now playing
song = null, // Auto set now playing
setSleepTimerEnable = true,
changeMainLyricsProviderEnable = true,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import com.maxrave.simpmusic.utils.collectResource
import com.maxrave.simpmusic.viewModel.base.BaseViewModel
import com.maxrave.simpmusic.viewModel.uiState.LocalPlaylistState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -59,7 +58,6 @@ import org.koin.android.annotation.KoinViewModel
import org.koin.core.component.inject
import java.time.LocalDateTime

@OptIn(FlowPreview::class)
@UnstableApi
@KoinViewModel
class LocalPlaylistViewModel(
Expand Down Expand Up @@ -453,21 +451,21 @@ class LocalPlaylistViewModel(
}

@UnstableApi
fun downloadFullPlaylistState(id: Long) {
fun downloadFullPlaylistState(id: Long, listJob: List<String>) {
viewModelScope.launch {
downloadUtils.downloads.collect { download ->
_uiState.update { ui ->
ui.copy(downloadState =
if (listJob.value.all { download[it.videoId]?.state == Download.STATE_COMPLETED }) {
if (listJob.all { download[it]?.state == Download.STATE_COMPLETED }) {
mainRepository.updateLocalPlaylistDownloadState(
STATE_DOWNLOADED,
id,
)
STATE_DOWNLOADED
} else if (listJob.value.all {
download[it.videoId]?.state == Download.STATE_QUEUED ||
download[it.videoId]?.state == Download.STATE_DOWNLOADING ||
download[it.videoId]?.state == Download.STATE_COMPLETED
} else if (listJob.all {
download[it]?.state == Download.STATE_QUEUED ||
download[it]?.state == Download.STATE_DOWNLOADING ||
download[it]?.state == Download.STATE_COMPLETED
}
) {
mainRepository.updateLocalPlaylistDownloadState(
Expand Down Expand Up @@ -885,7 +883,7 @@ class LocalPlaylistViewModel(
}
}

fun downloadTracks(listJob: List<String>) {
private fun downloadTracks(listJob: List<String>) {
viewModelScope.launch {
listJob.forEach { videoId ->
mainRepository.getSongById(videoId).singleOrNull()?.let { song ->
Expand All @@ -903,7 +901,7 @@ class LocalPlaylistViewModel(
val listJob = fullTracks.filter { it.downloadState != STATE_DOWNLOADED }.map { it.videoId }
if (listJob.isNotEmpty()) {
downloadTracks(listJob)
downloadFullPlaylistState(uiState.value.id)
downloadFullPlaylistState(uiState.value.id, listJob)
} else {
makeToast(getString(R.string.playlist_is_empty))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.maxrave.simpmusic.extension.toTrack
import com.maxrave.simpmusic.service.SleepTimerState
import com.maxrave.simpmusic.service.test.download.MusicDownloadService
import com.maxrave.simpmusic.viewModel.base.BaseViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand Down Expand Up @@ -47,6 +48,8 @@ class NowPlayingBottomSheetViewModel(
)
val uiState: StateFlow<NowPlayingBottomSheetUIState> get() = _uiState.asStateFlow()

private var getSongAsFlow: Job? = null

init {
viewModelScope.launch {
val sleepTimerJob = launch {
Expand Down Expand Up @@ -98,7 +101,8 @@ class NowPlayingBottomSheetViewModel(
}

private fun getSongEntityFlow(videoId: String) {
viewModelScope.launch {
getSongAsFlow?.cancel()
getSongAsFlow = viewModelScope.launch {
mainRepository.getSongAsFlow(videoId).collectLatest {
_uiState.value = _uiState.value.copy(songEntity = it)
}
Expand Down Expand Up @@ -209,7 +213,9 @@ class NowPlayingBottomSheetViewModel(
shareIntent.type = "text/plain"
val url = "https://music.youtube.com/watch?v=${songEntity.videoId}"
shareIntent.putExtra(Intent.EXTRA_TEXT, url)
val chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_url))
val chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_url)).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
application.startActivity(chooserIntent)
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
android = "8.6.1"
android = "8.7.2"
kotlin = "2.0.0"
serialization = "2.0.0"
ksp = "2.0.0-1.0.22"
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ object YouTube {
ytMusic.proxy = value
}

private val listPipedInstances = listOf(
"https://pipedapi.nosebs.ru",
"https://pipedapi.kavin.rocks",
"https://pipedapi.tokhmi.xyz",
"https://pipedapi.syncpundit.io",
"https://pipedapi.leptons.xyz",
"https://pipedapi.r4fo.com",
"https://yapi.vyper.me",
"https://pipedapi-libre.kavin.rocks"
)

/**
* Search for a song, album, artist, playlist, etc.
* @param query the search query
Expand Down Expand Up @@ -1223,6 +1234,7 @@ object YouTube {
playlistId: String? = null,
): Result<Triple<String?, PlayerResponse, MediaType>> =
runCatching {
var error: String? = null
val cpn =
(1..16)
.map {
Expand All @@ -1235,6 +1247,7 @@ object YouTube {
}.joinToString("")
val playerResponse =
ytMusic.player(ANDROID_MUSIC, videoId, playlistId, cpn).body<PlayerResponse>()
println("Player Response " + playerResponse)
// val ytScrapeInitial: YouTubeInitialPage = ytMusic.player(WEB, videoId, playlistId, cpn).body<YouTubeInitialPage>()
println("Thumbnails " + playerResponse.videoDetails?.thumbnail)
val firstThumb =
Expand All @@ -1244,11 +1257,13 @@ object YouTube {
?.firstOrNull()
val thumbnails =
if (firstThumb?.height == firstThumb?.width && firstThumb != null) MediaType.Song else MediaType.Video
println("Player Response " + playerResponse.streamingData?.formats?.map { Pair(it.itag, it.isAudio) })
println("Player Response " + playerResponse.streamingData?.adaptiveFormats?.map { Pair(it.itag, it.isAudio) })
val formatList = playerResponse.streamingData?.formats?.map { Pair(it.itag, it.isAudio) }
println("Player Response " + formatList)
val adaptiveFormatsList = playerResponse.streamingData?.adaptiveFormats?.map { Pair(it.itag, it.isAudio) }
println("Player Response " + adaptiveFormatsList)

// println( playerResponse.streamingData?.adaptiveFormats?.findLast { it.itag == 251 }?.mimeType.toString())
if (playerResponse.playabilityStatus.status == "OK") {
if (playerResponse.playabilityStatus.status == "OK" && (formatList != null || adaptiveFormatsList != null)) {
return@runCatching Triple(
cpn,
playerResponse.copy(
Expand All @@ -1257,24 +1272,33 @@ object YouTube {
thumbnails,
)
} else {
val piped = ytMusic.pipedStreams(videoId, "pipedapi.kavin.rocks").body<PipedResponse>()
val audioStreams = piped.audioStreams
val videoStreams = piped.videoStreams
val stream = audioStreams + videoStreams
return@runCatching Triple(
null,
playerResponse.copy(
streamingData =
PlayerResponse.StreamingData(
formats = stream.toListFormat(),
adaptiveFormats = stream.toListFormat(),
expiresInSeconds = 0,
for (instance in listPipedInstances) {
try {
val piped = ytMusic.pipedStreams(videoId, instance).body<PipedResponse>()
val audioStreams = piped.audioStreams
val videoStreams = piped.videoStreams
val stream = audioStreams + videoStreams
return@runCatching Triple(
null,
playerResponse.copy(
streamingData =
PlayerResponse.StreamingData(
formats = stream.toListFormat(),
adaptiveFormats = stream.toListFormat(),
expiresInSeconds = 0,
),
videoDetails = playerResponse.videoDetails?.copy(),
),
videoDetails = playerResponse.videoDetails?.copy(),
),
thumbnails,
)
thumbnails,
)
} catch (e: Exception) {
e.printStackTrace()
error = e.message
continue
}
}
}
throw Exception(error ?: "Unknown error")
}

suspend fun updateWatchTime(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ class Ytmusic {
suspend fun pipedStreams(
videoId: String,
pipedInstance: String,
) = httpClient.get("https://$pipedInstance/streams/$videoId") {
) = httpClient.get("$pipedInstance/streams/$videoId") {
contentType(ContentType.Application.Json)
}

Expand Down
Loading

0 comments on commit 56f891c

Please sign in to comment.