From 5a4187d85d686588b6e311efd3bf7ccc0fcae713 Mon Sep 17 00:00:00 2001 From: Anil Kumar Beesetti Date: Sat, 7 Dec 2024 12:32:16 +0530 Subject: [PATCH 1/2] use coroutines to generte thumbnails in parallel --- .../media/sync/LocalMediaInfoSynchronizer.kt | 84 +++++++++---------- .../core/media/sync/MediaInfoSynchronizer.kt | 2 +- .../screens/media/MediaPickerViewModel.kt | 4 +- .../mediaFolder/MediaPickerFolderViewModel.kt | 4 +- 4 files changed, 42 insertions(+), 52 deletions(-) diff --git a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt index 55e451329..a4ada3caf 100644 --- a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt +++ b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt @@ -17,15 +17,14 @@ import io.github.anilbeesetti.nextlib.mediainfo.AudioStream import io.github.anilbeesetti.nextlib.mediainfo.MediaInfoBuilder import io.github.anilbeesetti.nextlib.mediainfo.SubtitleStream import io.github.anilbeesetti.nextlib.mediainfo.VideoStream -import java.io.File -import java.io.FileOutputStream -import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import javax.inject.Inject class LocalMediaInfoSynchronizer @Inject constructor( private val mediumDao: MediumDao, @@ -34,54 +33,49 @@ class LocalMediaInfoSynchronizer @Inject constructor( @Dispatcher(NextDispatchers.Default) private val dispatcher: CoroutineDispatcher, ) : MediaInfoSynchronizer { - private val media = MutableSharedFlow() - - override suspend fun addMedia(uri: Uri) = media.emit(uri) + override fun syncMediaInfoForMediumUri(uri: Uri) { + updateMediaInfo(uri) + } - private suspend fun sync(): Unit = withContext(dispatcher) { - media.collect { mediumUri -> - val medium = mediumDao.getWithInfo(mediumUri.toString()) ?: return@collect - if (medium.mediumEntity.thumbnailPath?.let { File(it) }?.exists() == true) { - return@collect - } + private fun updateMediaInfo(mediumUri: Uri) = applicationScope.launch(dispatcher) { + val medium = mediumDao.getWithInfo(mediumUri.toString()) ?: return@launch - val mediaInfo = runCatching { - MediaInfoBuilder().from(context = context, uri = mediumUri).build() ?: throw NullPointerException() - }.onFailure { e -> - e.printStackTrace() - Log.d(TAG, "sync: MediaInfoBuilder exception", e) - }.getOrNull() ?: return@collect + val thumbnailExists = medium.mediumEntity.thumbnailPath?.let { File(it) }?.exists() == true + val isMediaSizeChanged = File(medium.mediumEntity.path).length() != medium.mediumEntity.size + if (thumbnailExists && !isMediaSizeChanged) return@launch - val thumbnail = runCatching { mediaInfo.getFrame() }.getOrNull() - mediaInfo.release() + val mediaInfo = runCatching { + MediaInfoBuilder().from(context = context, uri = mediumUri).build() ?: throw NullPointerException() + }.onFailure { e -> + e.printStackTrace() + Log.d(TAG, "sync: MediaInfoBuilder exception", e) + }.getOrNull() ?: return@launch - val videoStreamInfo = mediaInfo.videoStream?.toVideoStreamInfoEntity(medium.mediumEntity.uriString) - val audioStreamsInfo = mediaInfo.audioStreams.map { - it.toAudioStreamInfoEntity(medium.mediumEntity.uriString) - } - val subtitleStreamsInfo = mediaInfo.subtitleStreams.map { - it.toSubtitleStreamInfoEntity(medium.mediumEntity.uriString) - } - val thumbnailPath = thumbnail?.saveTo( - storageDir = context.thumbnailCacheDir, - quality = 40, - fileName = medium.mediumEntity.mediaStoreId.toString(), - ) + val thumbnail = runCatching { mediaInfo.getFrame() }.getOrNull() + mediaInfo.release() - mediumDao.upsert( - medium.mediumEntity.copy( - format = mediaInfo.format, - thumbnailPath = thumbnailPath, - ), - ) - videoStreamInfo?.let { mediumDao.upsertVideoStreamInfo(it) } - audioStreamsInfo.onEach { mediumDao.upsertAudioStreamInfo(it) } - subtitleStreamsInfo.onEach { mediumDao.upsertSubtitleStreamInfo(it) } + val videoStreamInfo = mediaInfo.videoStream?.toVideoStreamInfoEntity(medium.mediumEntity.uriString) + val audioStreamsInfo = mediaInfo.audioStreams.map { + it.toAudioStreamInfoEntity(medium.mediumEntity.uriString) } - } + val subtitleStreamsInfo = mediaInfo.subtitleStreams.map { + it.toSubtitleStreamInfoEntity(medium.mediumEntity.uriString) + } + val thumbnailPath = thumbnail?.saveTo( + storageDir = context.thumbnailCacheDir, + quality = 40, + fileName = medium.mediumEntity.mediaStoreId.toString(), + ) - init { - applicationScope.launch { sync() } + mediumDao.upsert( + medium.mediumEntity.copy( + format = mediaInfo.format, + thumbnailPath = thumbnailPath, + ), + ) + videoStreamInfo?.let { mediumDao.upsertVideoStreamInfo(it) } + audioStreamsInfo.onEach { mediumDao.upsertAudioStreamInfo(it) } + subtitleStreamsInfo.onEach { mediumDao.upsertSubtitleStreamInfo(it) } } companion object { diff --git a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/MediaInfoSynchronizer.kt b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/MediaInfoSynchronizer.kt index d4bdb6b3e..181c17cdf 100644 --- a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/MediaInfoSynchronizer.kt +++ b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/MediaInfoSynchronizer.kt @@ -4,5 +4,5 @@ import android.net.Uri interface MediaInfoSynchronizer { - suspend fun addMedia(uri: Uri) + fun syncMediaInfoForMediumUri(uri: Uri) } diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/media/MediaPickerViewModel.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/media/MediaPickerViewModel.kt index 7c9c1c384..8fbe0b371 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/media/MediaPickerViewModel.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/media/MediaPickerViewModel.kt @@ -72,9 +72,7 @@ class MediaPickerViewModel @Inject constructor( } fun addToMediaInfoSynchronizer(uri: Uri) { - viewModelScope.launch { - mediaInfoSynchronizer.addMedia(uri) - } + mediaInfoSynchronizer.syncMediaInfoForMediumUri(uri) } fun renameVideo(uri: Uri, to: String) { diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt index 06d39c215..5531c4b24 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt @@ -62,9 +62,7 @@ class MediaPickerFolderViewModel @Inject constructor( } fun addToMediaInfoSynchronizer(uri: Uri) { - viewModelScope.launch { - mediaInfoSynchronizer.addMedia(uri) - } + mediaInfoSynchronizer.syncMediaInfoForMediumUri(uri) } fun renameVideo(uri: Uri, to: String) { From 5fa2a160c732c63723ff863fe6f9692560f69e52 Mon Sep 17 00:00:00 2001 From: Anil Kumar Beesetti Date: Sat, 7 Dec 2024 12:51:29 +0530 Subject: [PATCH 2/2] lint: run ktlintFormat --- .../core/media/sync/LocalMediaInfoSynchronizer.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt index a4ada3caf..afd84c72f 100644 --- a/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt +++ b/core/media/src/main/java/dev/anilbeesetti/nextplayer/core/media/sync/LocalMediaInfoSynchronizer.kt @@ -17,14 +17,14 @@ import io.github.anilbeesetti.nextlib.mediainfo.AudioStream import io.github.anilbeesetti.nextlib.mediainfo.MediaInfoBuilder import io.github.anilbeesetti.nextlib.mediainfo.SubtitleStream import io.github.anilbeesetti.nextlib.mediainfo.VideoStream +import java.io.File +import java.io.FileOutputStream +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.File -import java.io.FileOutputStream -import javax.inject.Inject class LocalMediaInfoSynchronizer @Inject constructor( private val mediumDao: MediumDao,