Skip to content

Commit

Permalink
90% Local Playlist Compose
Browse files Browse the repository at this point in the history
  • Loading branch information
maxrave-dev committed Nov 5, 2024
1 parent c6ef517 commit a388906
Show file tree
Hide file tree
Showing 64 changed files with 5,223 additions and 3,929 deletions.
4 changes: 4 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

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

1 change: 1 addition & 0 deletions .idea/ktlint-plugin.xml

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

Empty file.
8 changes: 5 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@ plugins {

android {
namespace = "com.maxrave.simpmusic"
compileSdk = 34
compileSdk = 35

defaultConfig {
applicationId = "com.maxrave.simpmusic"
minSdk = 26
targetSdk = 35
versionCode = 20
versionName = "0.2.3-hf2"
versionCode = 21
versionName = "0.2.4"
vectorDrawables.useSupportLibrary = true

ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("KOIN_CONFIG_CHECK", "true")
arg("KOIN_USE_COMPOSE_VIEWMODEL","true")
}

resourceConfigurations +=
Expand Down Expand Up @@ -180,6 +181,7 @@ dependencies {
implementation(composeBom)
androidTestImplementation(composeBom)
implementation(libs.compose.material3.lib)
implementation(libs.compose.material3.sizeclass)
implementation(libs.compose.ui)
implementation(libs.compose.material.ripple)
implementation(libs.compose.material.icons.core)
Expand Down
38 changes: 19 additions & 19 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.maxrave.simpmusic",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 20,
"versionName": "0.2.3-hf2",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"minSdkVersionForDexing": 26
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.maxrave.simpmusic",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 21,
"versionName": "0.2.4",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"minSdkVersionForDexing": 26
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.media3.common.util.UnstableApi
import cat.ereza.customactivityoncrash.config.CaocConfig
import com.maxrave.simpmusic.di.databaseModule
import com.maxrave.simpmusic.di.mediaServiceModule
import com.maxrave.simpmusic.di.viewModelModule
import com.maxrave.simpmusic.ui.MainActivity
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
Expand All @@ -29,6 +30,7 @@ class SimpMusicApplication :
modules(
databaseModule,
mediaServiceModule,
viewModelModule
)
workManagerFactory()
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/maxrave/simpmusic/common/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ object Config {
const val PLAYER_CACHE = "playerCache"
const val DOWNLOAD_CACHE = "downloadCache"
const val CANVAS_CACHE = "canvasCache"
const val SERVICE_SCOPE = "serviceScope"

val REMOVED_SONG_DATE_TIME = LocalDateTime.of(2003, Month.AUGUST, 26, 3, 0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@ interface DatabaseDao {
state: Int,
)

@Query("UPDATE local_playlist SET synced_with_youtube_playlist = 0, youtube_sync_state = 0, youtubePlaylistId = NULL WHERE id = :id")
suspend fun unsyncLocalPlaylist(id: Long)

@Query("SELECT downloadState FROM local_playlist WHERE id = :id")
fun getDownloadStateFlowOfLocalPlaylist(id: Long): Flow<Int>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ class LocalDataSource(
suspend fun insertPairSongLocalPlaylist(pairSongLocalPlaylist: PairSongLocalPlaylist) =
databaseDao.insertPairSongLocalPlaylist(pairSongLocalPlaylist)

suspend fun unsyncLocalPlaylist(id: Long) = databaseDao.unsyncLocalPlaylist(id)

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

suspend fun getPlaylistPairSongByOffset(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.maxrave.simpmusic.data.manager

import android.content.Context
import com.maxrave.simpmusic.data.manager.base.BaseManager

class LoadMoreManager(
private val context: Context,
) : BaseManager(context) {
override val tag: String
get() = this.javaClass.simpleName
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ import com.maxrave.kotlinytmusicscraper.YouTube
import com.maxrave.kotlinytmusicscraper.models.MusicShelfRenderer
import com.maxrave.simpmusic.R
import com.maxrave.simpmusic.common.DownloadState
import com.maxrave.simpmusic.data.db.Converters
import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity
import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity.YouTubeSyncState.Synced
import com.maxrave.simpmusic.data.db.entities.LocalPlaylistEntity.YouTubeSyncState.Syncing
import com.maxrave.simpmusic.data.db.entities.PairSongLocalPlaylist
import com.maxrave.simpmusic.data.db.entities.SetVideoIdEntity
import com.maxrave.simpmusic.data.db.entities.SongEntity
import com.maxrave.simpmusic.data.manager.base.BaseManager
import com.maxrave.simpmusic.data.model.browse.album.Track
import com.maxrave.simpmusic.data.model.browse.playlist.PlaylistBrowse
import com.maxrave.simpmusic.data.parser.parseSetVideoId
import com.maxrave.simpmusic.extension.toListTrack
import com.maxrave.simpmusic.extension.toListVideoId
import com.maxrave.simpmusic.extension.toSongEntity
import com.maxrave.simpmusic.extension.toTrack
import com.maxrave.simpmusic.pagination.localPlaylistPaging.LocalPlaylistPagingSource
import com.maxrave.simpmusic.utils.LocalResource
import com.maxrave.simpmusic.viewModel.FilterState
Expand All @@ -43,7 +47,8 @@ class LocalPlaylistManager(

fun downloadStateFlow(id: Long): Flow<Int> = localDataSource.getDownloadStateFlowOfLocalPlaylist(id)

fun listTrackFlow(id: Long): Flow<List<String>> = localDataSource.getListTracksFlowOfLocalPlaylist(id).map { it ?: emptyList() }
fun listTrackFlow(id: Long): Flow<List<String>> = localDataSource.getListTracksFlowOfLocalPlaylist(id)
.map { Converters().fromString(it?.firstOrNull()) ?: emptyList() }

fun getTracksPaging(
id: Long,
Expand All @@ -64,6 +69,37 @@ class LocalPlaylistManager(
).flow
}

suspend fun getFullPlaylistTracks(id: Long): List<SongEntity> {
val playlist = localDataSource.getLocalPlaylist(id)
val tracks = mutableListOf<SongEntity>()
var currentPage = 0
while (true) {
val pairs =
localDataSource.getPlaylistPairSongByOffset(
playlistId = id,
filterState = FilterState.OlderFirst,
offset = currentPage,
totalCount = playlist.tracks?.size ?: 0,
)
if (pairs.isNullOrEmpty()) {
break
}
val songs =
localDataSource
.getSongByListVideoIdFull(
pairs.map { it.songId },
)
val idValue = songs.associateBy { it.videoId }
val sorted =
pairs.mapNotNull {
idValue[it.songId]
}
tracks.addAll(sorted)
currentPage++
}
return tracks
}

suspend fun insertLocalPlaylist(localPlaylist: LocalPlaylistEntity): Flow<LocalResource<String>> =
wrapMessageResource(
successMessage = getString(R.string.added_local_playlist),
Expand Down Expand Up @@ -112,6 +148,15 @@ class LocalPlaylistManager(
localDataSource.updateLocalPlaylistThumbnail(id = id, thumbnail = newThumbnail)
}

suspend fun updateDownloadState(
id: Long,
downloadState: Int,
) = wrapMessageResource(
successMessage = getString(R.string.updated),
) {
localDataSource.updateLocalPlaylistDownloadState(id = id, downloadState = downloadState)
}

suspend fun syncYouTubePlaylistToLocalPlaylist(playlist: PlaylistBrowse): Flow<LocalResource<String>> =
flow<LocalResource<String>> {
emit(LocalResource.Loading())
Expand Down Expand Up @@ -221,11 +266,12 @@ class LocalPlaylistManager(
/**
* Sync local playlist to YouTube playlist
* return youtubePlaylistId
* @param LocalPlaylistEntity
* @param playlistId
* @return Flow<LocalResource<String>>
*/
suspend fun syncLocalPlaylistToYouTubePlaylist(playlist: LocalPlaylistEntity) =
suspend fun syncLocalPlaylistToYouTubePlaylist(playlistId: Long) =
wrapResultResource {
val playlist = localDataSource.getLocalPlaylist(playlistId)
YouTube
.createPlaylist(
playlist.title,
Expand All @@ -235,6 +281,73 @@ class LocalPlaylistManager(
}
}

suspend fun unsyncLocalPlaylist(id: Long) =
wrapMessageResource(
successMessage = getString(R.string.unsynced),
) {
localDataSource.unsyncLocalPlaylist(id)
}

suspend fun updateSyncState(
id: Long,
syncState: Int,
) = wrapMessageResource(
successMessage = getString(R.string.synced),
) {
localDataSource.updateLocalPlaylistYouTubePlaylistSyncState(id, syncState)
}

suspend fun updateYouTubePlaylistId(
id: Long,
youtubePlaylistId: String,
) = wrapMessageResource(
successMessage = getString(R.string.updated),
) {
localDataSource.updateLocalPlaylistYouTubePlaylistId(id, youtubePlaylistId)
}

suspend fun updateListTrackSynced(id: Long) =
flow<Boolean> {
val localPlaylist = localDataSource.getLocalPlaylist(id)
val tracks = localPlaylist.tracks ?: emptyList()
val currentTracks = tracks.toMutableList()
localPlaylist.youtubePlaylistId?.let { ytId ->
Log.d(tag, "updateListTrackSynced: $ytId")
YouTube.getYouTubePlaylistFullTracksWithSetVideoId(ytId).onSuccess { list ->
Log.d(tag, "updateListTrackSynced: onSuccess ${list.map { it.first.title }}")
val newTrack = list.map { it.first }.toListTrack().map { it.videoId }.toMutableSet().subtract(tracks.toMutableSet())
val newTrackList = list.filter { newTrack.contains(it.first.id) }
Log.w(tag, "updateListTrackSynced: newTrackList ${newTrackList.map { it.first.title }}")
newTrackList.forEach { new ->
localDataSource.insertSong(new.first.toTrack().toSongEntity())
Log.i(tag, "insertSong: ${new.first.toTrack().toSongEntity()}")
localDataSource.insertPairSongLocalPlaylist(
PairSongLocalPlaylist(
playlistId = id,
songId = new.first.id,
position = currentTracks.size,
inPlaylist = LocalDateTime.now()
)
)
localDataSource.insertSetVideoId(
SetVideoIdEntity(
videoId = new.first.id, setVideoId = new.second, youtubePlaylistId = ytId
)
)
currentTracks.add(new.first.id)
}
localDataSource.updateLocalPlaylistTracks(currentTracks, id).let {
emit(true)
}
}.onFailure { e ->
Log.e(tag, "updateListTrackSynced: onFailure ${e.message}")
e.printStackTrace()
emit(false)
}
}
emit(false)
}

// Update
suspend fun addTrackToLocalPlaylist(
id: Long,
Expand Down Expand Up @@ -315,4 +428,28 @@ class LocalPlaylistManager(
}
}
}.flowOn(Dispatchers.IO)

suspend fun getSuggestionsTrackForPlaylist(
id: Long
): Flow<LocalResource<Pair<String?, List<Track>>>> = flow {
val localPlaylist = localDataSource.getLocalPlaylist(id)
val ytPlaylistId = localPlaylist.youtubePlaylistId ?: return@flow

YouTube.getSuggestionsTrackForPlaylist(ytPlaylistId).onSuccess { data ->
val listSongItem = data?.second?.map { it.toTrack() }
if (data != null && !listSongItem.isNullOrEmpty()) {
emit(LocalResource.Success(
Pair(
data.first,
listSongItem
)
))
} else {
emit(LocalResource.Error("List suggestions is null"))
}
}.onFailure { e ->
e.printStackTrace()
emit(LocalResource.Error(e.message ?: "Error"))
}
}
}
Loading

0 comments on commit a388906

Please sign in to comment.