Skip to content

Commit

Permalink
Merge pull request #194 from andannn/vlc_player_for_destop_app
Browse files Browse the repository at this point in the history
move file to commonMain
  • Loading branch information
andannn authored Nov 25, 2024
2 parents 0dc40ee + 18d9810 commit 5172c83
Show file tree
Hide file tree
Showing 15 changed files with 64 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class PlayerWrapperImpl : PlayerWrapper {

private val _playListFlow = MutableSharedFlow<List<MediaItem>>(1)

private val playerProgressUpdater: CoroutineTicker = CoroutineTicker(delayMs = 1000 / 30L) {
private val playerProgressUpdater: CoroutineTimer = CoroutineTimer(delayMs = 1000 / 30L) {
_playerStateFlow.getAndUpdate { old ->
if (_player == null) {
return@getAndUpdate old
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import com.andannn.melodify.core.player.MediaBrowserManager
import com.andannn.melodify.core.player.MediaBrowserManagerImpl
import com.andannn.melodify.core.player.PlayerWrapper
import com.andannn.melodify.core.player.PlayerWrapperImpl
import com.andannn.melodify.core.player.SleepTimerController
import com.andannn.melodify.core.player.SleepTimerControllerImpl
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

actual val playerModule = module {
internal actual val platformPlayerModule: Module = module {
singleOf(::PlayerWrapperImpl).bind(PlayerWrapper::class)
singleOf(::MediaBrowserManagerImpl).bind(MediaBrowserManager::class)
singleOf(::SleepTimerControllerImpl).bind(SleepTimerController::class)
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
package com.andannn.melodify.core.player

import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext

private const val TAG = "CoroutineTicker"

class CoroutineTicker(
class CoroutineTimer(
private val delayMs: Long = 1000 / 30L,
val action: () -> Unit,
) : CoroutineScope {
private var jobTracker: Job? = null

override val coroutineContext: CoroutineContext get() = Dispatchers.Main
override val coroutineContext: CoroutineContext = Dispatchers.Main

fun startTicker() {
if (jobTracker != null) {
Napier.d(tag = TAG) { "startTicker: already started ${this.hashCode()}" }
return
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package com.andannn.melodify.core.player.di

import com.andannn.melodify.core.player.SleepTimerController
import com.andannn.melodify.core.player.SleepTimerControllerImpl
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

expect val playerModule : Module
val playerModule : Module = module {
includes(platformPlayerModule)
singleOf(::SleepTimerControllerImpl).bind(SleepTimerController::class)
}

internal expect val platformPlayerModule : Module
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package com.andannn.melodify.core.player.di
import org.koin.core.module.Module
import org.koin.dsl.module

actual val playerModule: Module = module {
internal actual val platformPlayerModule: Module = module {

}
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
package com.andannn.melodify.core.player.di

import org.koin.dsl.module

actual val playerModule = module {

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import com.andannn.melodify.core.database.dao.MediaLibraryDao
import com.andannn.melodify.core.syncer.model.MediaDataModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
Expand All @@ -15,7 +16,30 @@ import kotlinx.coroutines.withContext

class MediaLibraryScannerImpl(
private val app: Application,
private val mediaLibraryDao: MediaLibraryDao,
) : MediaLibraryScanner {

override suspend fun scanMediaDataAndSyncDatabase(): Unit = coroutineScope {
val musicDataDeferred = async { getAllMusicData() }
val albumDataDeferred = async { getAllAlbumData() }
val artistDataDeferred = async { getAllArtistData() }
val genreDataDeferred = async { getAllGenreData() }

val mediaData = MediaDataModel(
audioData = musicDataDeferred.await(),
albumData = albumDataDeferred.await(),
artistData = artistDataDeferred.await(),
genreData = genreDataDeferred.await(),
)

mediaLibraryDao.clearAndInsertLibrary(
mediaData.albumData.toAlbumEntity(),
mediaData.artistData.toArtistEntity(),
mediaData.genreData.toGenreEntity(),
mediaData.audioData.toMediaEntity(),
)
}

private suspend fun getAllMusicData() =
app.contentResolver.query2(
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
Expand Down Expand Up @@ -44,20 +68,6 @@ class MediaLibraryScannerImpl(
parseGenreInfoCursor(cursor)
} ?: emptyList()

override suspend fun scanMediaData() = coroutineScope {
val musicDataDeferred = async { getAllMusicData() }
val albumDataDeferred = async { getAllAlbumData() }
val artistDataDeferred = async { getAllArtistData() }
val genreDataDeferred = async { getAllGenreData() }

MediaDataModel(
audioData = musicDataDeferred.await(),
albumData = albumDataDeferred.await(),
artistData = artistDataDeferred.await(),
genreData = genreDataDeferred.await(),
)
}

private fun parseGenreInfoCursor(cursor: Cursor): List<com.andannn.melodify.core.syncer.model.GenreData> {
val itemList = mutableListOf<com.andannn.melodify.core.syncer.model.GenreData>()

Expand Down Expand Up @@ -92,7 +102,6 @@ class MediaLibraryScannerImpl(
val numTracksIndex = cursor.getColumnIndex(MediaStore.Audio.Media.NUM_TRACKS)
val bitrateIndex = cursor.getColumnIndex(MediaStore.Audio.Media.BITRATE)
val yearIndex = cursor.getColumnIndex(MediaStore.Audio.Media.YEAR)
val trackIndex = cursor.getColumnIndex(MediaStore.Audio.Media.TRACK)
val composerIndex = cursor.getColumnIndex(MediaStore.Audio.Media.COMPOSER)
val genreIndex = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
cursor.getColumnIndex(MediaStore.Audio.Media.GENRE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package com.andannn.melodify.core.syncer.di
import com.andannn.melodify.core.syncer.MediaLibraryScanner
import com.andannn.melodify.core.syncer.MediaLibraryScannerImpl
import com.andannn.melodify.core.syncer.MediaLibrarySyncer
import com.andannn.melodify.core.syncer.MediaLibrarySyncerImpl
import com.andannn.melodify.core.syncer.MediaLibrarySyncerWrapper
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

actual val syncerModule: Module = module {
singleOf(::MediaLibrarySyncerImpl).bind(MediaLibrarySyncer::class)
singleOf(::MediaLibrarySyncerWrapper).bind(MediaLibrarySyncer::class)
singleOf(::MediaLibraryScannerImpl).bind(MediaLibraryScanner::class)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.andannn.melodify.core.syncer

import com.andannn.melodify.core.syncer.model.MediaDataModel

interface MediaLibraryScanner {
suspend fun scanMediaData(): MediaDataModel
suspend fun scanMediaDataAndSyncDatabase()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ interface MediaLibrarySyncer {
suspend fun syncMediaLibrary(): Boolean
}

internal class MediaLibrarySyncerImpl(
internal class MediaLibrarySyncerWrapper(
private val mediaLibraryScanner: MediaLibraryScanner,
private val mediaLibraryDao: MediaLibraryDao,
) : MediaLibrarySyncer {
override suspend fun syncMediaLibrary(): Boolean {
Napier.d(tag = TAG) { "Syncing media library" }
Expand All @@ -36,14 +35,7 @@ internal class MediaLibrarySyncerImpl(

private suspend fun syncMediaLibraryInternal(): Boolean {
try {
val mediaData = mediaLibraryScanner.scanMediaData()

mediaLibraryDao.clearAndInsertLibrary(
mediaData.albumData.toAlbumEntity(),
mediaData.artistData.toArtistEntity(),
mediaData.genreData.toGenreEntity(),
mediaData.audioData.toMediaEntity(),
)
mediaLibraryScanner.scanMediaDataAndSyncDatabase()
return true
} catch (e: Exception) {
Napier.d(tag = TAG) { "Failed to sync media library: $e" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package com.andannn.melodify.core.syncer.di

import com.andannn.melodify.core.syncer.MediaLibrarySyncer
import com.andannn.melodify.core.syncer.MediaLibrarySyncerImpl
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

expect val syncerModule: Module
//= module {
// singleOf(::MediaLibrarySyncerImpl).bind(MediaLibrarySyncer::class)
//}
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,28 @@ class MediaLibraryScannerImpl(
private val mediaLibraryDao: MediaLibraryDao,
private val userSettingPreferences: UserSettingPreferences,
) : MediaLibraryScanner {
override suspend fun scanMediaData(): MediaDataModel {
override suspend fun scanMediaDataAndSyncDatabase() {

// 1: Get All media from database
// 2: Scan all files in library path and generated Key (generate hash from file path and last modify date).
// 3: Loop through all files in library path and create new media data list. rules:
// - If key exist in db, map db entity to AudioData (skip extract metadata).
// - If id not exist in db. extract metadata from file and create new AudioData
// 4: Group album from created AudioData list
// 4: Group album from created AlbumData list
// 5: Group artist from created AudioData list
// 6: Group genre from created AudioData list
// 6: Group genre from created GenreData list
// 7: Insert all data to database.

val allMediaEntity = mediaLibraryDao.getAllMediaFlow().first()
val mediaInDb = allMediaEntity.associateBy { it.id }

// TODO Get Path from DataStore after implement library path setting feature.
// val libraryPathSet = userSettingPreferences.userDate.first().libraryPath
val libraryPathSet = setOf(
"/mnt/chromeos/MyFiles/Shared"
"/Users/jiangqn/Documents"
)

// TODO: Scan file in worker thread.
val audioFileWithLastModifyDateList = scanAllLibraryAudioFile(libraryPathSet)

Napier.d(tag = TAG) { "scanMediaData: ${audioFileWithLastModifyDateList.size} files found" }
Expand Down Expand Up @@ -119,15 +121,21 @@ class MediaLibraryScannerImpl(
)
}

return MediaDataModel(
val mediaData = MediaDataModel(
audioData = audioDataListWitId,
albumData = albumDataList,
artistData = artistDataList,
genreData = genreDataList,
)
}


// TODO: Incremental comparison and insertion into the database, deleting outdated data.
mediaLibraryDao.clearAndInsertLibrary(
mediaData.albumData.toAlbumEntity(),
mediaData.artistData.toArtistEntity(),
mediaData.genreData.toGenreEntity(),
mediaData.audioData.toMediaEntity(),
)
}
}

private fun MediaEntity.toAudioData() = AudioData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package com.andannn.melodify.core.syncer.di
import com.andannn.melodify.core.syncer.MediaLibraryScanner
import com.andannn.melodify.core.syncer.MediaLibraryScannerImpl
import com.andannn.melodify.core.syncer.MediaLibrarySyncer
import com.andannn.melodify.core.syncer.MediaLibrarySyncerImpl
import com.andannn.melodify.core.syncer.MediaLibrarySyncerWrapper
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module

actual val syncerModule: Module = module {
singleOf(::MediaLibrarySyncerImpl).bind(MediaLibrarySyncer::class)
singleOf(::MediaLibrarySyncerWrapper).bind(MediaLibrarySyncer::class)
singleOf(::MediaLibraryScannerImpl).bind(MediaLibraryScanner::class)
}

0 comments on commit 5172c83

Please sign in to comment.