Skip to content

Commit

Permalink
Local Playlist Shuffle
Browse files Browse the repository at this point in the history
  • Loading branch information
maxrave-dev committed Nov 9, 2024
1 parent 83e7c9c commit e2213ab
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 69 deletions.
11 changes: 11 additions & 0 deletions app/src/main/java/com/maxrave/simpmusic/data/db/Converters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ class Converters {
return gson.toJson(list)
}

// No use in database
fun fromListIntToString(list: List<Int>?): String? {
val gson = Gson()
return gson.toJson(list)
}

fun fromStringToListInt(value: String?): List<Int>? {
val listType: Type = object : TypeToken<ArrayList<Int?>?>() {}.type
return Gson().fromJson(value, listType)
}

@TypeConverter
fun fromStringToListTrack(value: String?): List<Track>? {
val listType: Type = object : TypeToken<List<Track?>?>() {}.type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ interface DatabaseDao {
@Query("SELECT * FROM pair_song_local_playlist WHERE playlistId = :playlistId")
suspend fun getPlaylistPairSong(playlistId: Long): List<PairSongLocalPlaylist>?

@Query("SELECT * FROM pair_song_local_playlist WHERE playlistId = :playlistId AND position in (:positionList)")
suspend fun getPlaylistPairSongByListPosition(playlistId: Long, positionList: List<Int>): List<PairSongLocalPlaylist>?

@Query(
"SELECT * FROM pair_song_local_playlist WHERE playlistId = :playlistId ORDER BY position " +
"ASC LIMIT 50 OFFSET :offset",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ class LocalDataSource(

suspend fun getPlaylistPairSong(playlistId: Long) = databaseDao.getPlaylistPairSong(playlistId)

suspend fun getPlaylistPairSongByListPosition(
playlistId: Long,
listPosition: List<Int>,
) = databaseDao.getPlaylistPairSongByListPosition(playlistId, listPosition)

suspend fun getPlaylistPairSongByOffset(
playlistId: Long,
offset: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ class LocalPlaylistManager(
return tracks
}

suspend fun getListTrackVideoId(id: Long): List<String> {
val playlist = localDataSource.getLocalPlaylist(id)
return playlist.tracks ?: emptyList()
}

suspend fun insertLocalPlaylist(localPlaylist: LocalPlaylistEntity): Flow<LocalResource<String>> =
wrapMessageResource(
successMessage = getString(R.string.added_local_playlist),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,14 @@ class MainRepository(
emit(localDataSource.getPlaylistPairSong(playlistId))
}.flowOn(Dispatchers.IO)

suspend fun getPlaylistPairSongByListPosition(
playlistId: Long,
listPosition: List<Int>,
): Flow<List<PairSongLocalPlaylist>?> =
flow {
emit(localDataSource.getPlaylistPairSongByListPosition(playlistId, listPosition))
}.flowOn(Dispatchers.IO)

suspend fun getPlaylistPairSongByOffset(
playlistId: Long,
offset: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.maxrave.simpmusic.common.MEDIA_CUSTOM_COMMAND
import com.maxrave.simpmusic.common.SPONSOR_BLOCK
import com.maxrave.simpmusic.data.dataStore.DataStoreManager
import com.maxrave.simpmusic.data.dataStore.DataStoreManager.Settings.TRUE
import com.maxrave.simpmusic.data.db.Converters
import com.maxrave.simpmusic.data.db.entities.NewFormatEntity
import com.maxrave.simpmusic.data.db.entities.SongEntity
import com.maxrave.simpmusic.data.model.browse.album.Track
Expand Down Expand Up @@ -91,6 +92,7 @@ class SimpleMediaServiceHandler(

@Suppress("ktlint:standard:property-naming")
private val TAG = "SimpleMediaServiceHandler"
private val converter = Converters()

private var loudnessEnhancer: LoudnessEnhancer? = null

Expand Down Expand Up @@ -860,6 +862,42 @@ class SimpleMediaServiceHandler(
}
}

fun shufflePlaylist(randomTrackIndex: Int = 0) {
val playlistId = _queueData.value?.playlistId ?: return
val firstPlayedTrack = _queueData.value?.firstPlayedTrack ?: return
coroutineScope.launch {
if (playlistId.startsWith(LOCAL_PLAYLIST_ID)) {
_stateFlow.value = StateSource.STATE_INITIALIZING
mainRepository.insertSong(firstPlayedTrack.toSongEntity()).collect {
Log.w(TAG, "Inserted song: ${firstPlayedTrack.title}")
}
clearMediaItems()
firstPlayedTrack.durationSeconds?.let {
mainRepository.updateDurationSeconds(it, firstPlayedTrack.videoId)
}
addMediaItem(firstPlayedTrack.toMediaItem(), playWhenReady = true)
val longId = playlistId.replace(LOCAL_PLAYLIST_ID, "").toLong()
val localPlaylist = mainRepository.getLocalPlaylist(longId).singleOrNull()
if (localPlaylist != null) {
Log.w(TAG, "shufflePlaylist: Local playlist track size ${localPlaylist.tracks?.size}")
val trackCount = localPlaylist.tracks?.size ?: return@launch
val listPosition = (0 until trackCount).toMutableList().apply {
remove(randomTrackIndex)
}
if (listPosition.size <= 0) return@launch
listPosition.shuffle()
_queueData.update {
it?.copy(
//After shuffle prefix is offset and list position
continuation = "SHUFFLE0_${converter.fromListIntToString(listPosition)}",
)
}
loadMore()
}
}
}
}

fun loadMore() {
// Separate local and remote data
// Local Add Prefix to PlaylistID to differentiate between local and remote
Expand All @@ -874,54 +912,83 @@ class SimpleMediaServiceHandler(
_stateFlow.value = StateSource.STATE_INITIALIZING
val longId = playlistId.replace(LOCAL_PLAYLIST_ID, "").toLong()
Log.w("Check loadMore", longId.toString())
val filter = if (continuation.startsWith(ASC)) FilterState.OlderFirst else FilterState.NewerFirst
val offset =
if (filter == FilterState.OlderFirst) {
continuation
.removePrefix(
ASC,
).toInt()
} else {
continuation.removePrefix(DESC).toInt()
}
val total =
mainRepository
.getLocalPlaylist(longId)
.firstOrNull()
?.tracks
?.size ?: 0
mainRepository
.getPlaylistPairSongByOffset(
if (continuation.startsWith("SHUFFLE")) {
val regex = Regex("(?<=SHUFFLE)\\d+(?=_)")
var offset = regex.find(continuation)?.value?.toInt() ?: return@launch
val posString = continuation.removePrefix("SHUFFLE${offset}_")
val listPosition = converter.fromStringToListInt(posString) ?: return@launch
val theLastLoad = 50*(offset + 1) >= listPosition.size
mainRepository.getPlaylistPairSongByListPosition(
longId,
offset,
filter,
total,
).singleOrNull()
?.let { pair ->
listPosition.subList(50*offset, if (theLastLoad) listPosition.size - 1 else 50*(offset + 1)),
).singleOrNull()?.let { pair ->
Log.w("Check loadMore response", pair.size.toString())
mainRepository.getSongsByListVideoId(pair.map { it.songId }).single().let { songs ->
if (songs.isNotEmpty()) {
delay(300)
loadMoreCatalog(songs.toArrayListTrack())
// Queue.setContinuation(
// playlistId,
// if (filter == FilterState.OlderFirst) ASC + (offset + 1) else DESC + (offset + 1).toString(),
// )
_queueData.value =
_queueData.value?.copy(
continuation =
offset++
_queueData.update {
if (!theLastLoad){
it?.copy(
continuation = "SHUFFLE${offset}_$posString",
)
} else {
it?.copy(
continuation = null
)
}
}
}
}
}
} else {
val filter = if (continuation.startsWith(ASC)) FilterState.OlderFirst else FilterState.NewerFirst
val offset =
if (filter == FilterState.OlderFirst) {
continuation
.removePrefix(
ASC,
).toInt()
} else {
continuation.removePrefix(DESC).toInt()
}
val total =
mainRepository
.getLocalPlaylist(longId)
.firstOrNull()
?.tracks
?.size ?: 0
mainRepository
.getPlaylistPairSongByOffset(
longId,
offset,
filter,
total,
).singleOrNull()
?.let { pair ->
Log.w("Check loadMore response", pair.size.toString())
mainRepository.getSongsByListVideoId(pair.map { it.songId }).single().let { songs ->
if (songs.isNotEmpty()) {
delay(300)
loadMoreCatalog(songs.toArrayListTrack())
_queueData.value =
_queueData.value?.copy(
continuation =
if (filter ==
FilterState.OlderFirst
) {
ASC + (offset + 1)
} else {
DESC + (offset + 1).toString()
},
)
)
} else {
_stateFlow.value = StateSource.STATE_INITIALIZED
}
}
// loadingMore.value = false
}
}
}
}
} else {
coroutineScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,13 @@ fun NowPlayingScreen(
.fillMaxWidth()
.wrapContentHeight(align = Alignment.CenterVertically)
.basicMarquee(animationMode = MarqueeAnimationMode.Immediately)
.focusable(),
.focusable()
.clickable {
navController.navigateSafe(
R.id.action_global_artistFragment,
bundleOf("channelId" to screenDataState.songInfoData?.authorId),
)
},
)
}
Spacer(modifier = Modifier.size(10.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -830,45 +830,34 @@ class LocalPlaylistViewModel(
)
}
is LocalPlaylistUIEvent.ShuffleClick -> {
val loadedList = lazyTrackPagingItems.value?.itemSnapshotList?.toList().let {
if (it.isNullOrEmpty()) {
viewModelScope.launch {
val listVideoId = localPlaylistManager.getListTrackVideoId(uiState.value.id)
log("ShuffleClick: uiState id ${uiState.value.id}", Log.DEBUG)
log("ShuffleClick: $listVideoId", Log.DEBUG)
if (listVideoId.isEmpty()) {
makeToast(getString(R.string.playlist_is_empty))
return
} else {
it.filterNotNull().toArrayListTrack()
return@launch
}
}
val random = loadedList.random()
setQueueData(
QueueData(
listTracks = loadedList,
firstPlayedTrack = random,
playlistId = LOCAL_PLAYLIST_ID + uiState.value.id,
playlistName = "${
getString(
R.string.playlist,
)
} \"${uiState.value.title}\"",
playlistType = PlaylistType.LOCAL_PLAYLIST,
continuation =
if (offset.value > 0) {
if (uiState.value.filterState == FilterState.OlderFirst) {
(ASC + offset.toString())
} else {
(DESC + offset)
}
} else {
null
},
val random = listVideoId.random()
val randomIndex = listVideoId.indexOf(random)
val firstPlayedTrack = mainRepository.getSongById(random).singleOrNull()?.toTrack() ?: return@launch
setQueueData(
QueueData(
listTracks = arrayListOf(firstPlayedTrack),
firstPlayedTrack = firstPlayedTrack,
playlistId = LOCAL_PLAYLIST_ID + uiState.value.id,
playlistName = "${
getString(
R.string.playlist,
)
} \"${uiState.value.title}\"",
playlistType = PlaylistType.LOCAL_PLAYLIST,
continuation = ""
)
)
)
loadMediaItem(
random,
Config.PLAYLIST_CLICK,
loadedList.indexOf(random)
)
shufflePlaylist(randomIndex)
}
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,9 @@ abstract class BaseViewModel(
}
}

fun shufflePlaylist(firstPlayIndex: Int = 0) {
simpleMediaServiceHandler.shufflePlaylist(firstPlayIndex)
}

fun getQueueData() = simpleMediaServiceHandler.queueData.value
}

0 comments on commit e2213ab

Please sign in to comment.