package com.suno.android.common_data.upload import android.net.Uri import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OutOfQuotaPolicy import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.workDataOf import com.suno.android.common_core_utils.Id import com.suno.android.common_core_utils.model.Url import com.suno.android.common_data.upload.model.UploadingFile import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.mapNotNull import java.util.UUID import javax.inject.Inject interface UploadsRepository { fun uploadAudioAsClip( localFile: Uri, name: String, ): Id fun getFileUploadStatusFlow( id: Id, ): Flow fun cancelUpload( id: Id, ) } class DefaultUploadsRepository @Inject constructor( private val workManager: WorkManager, ) : UploadsRepository { override fun uploadAudioAsClip( localFile: Uri, name: String, ): Id { val request = OneTimeWorkRequestBuilder().setExpedited( OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST, ) .setInputData( workDataOf( UploadFileAsClipWorker.Parameters.Input.FILE_URI to localFile.toString(), UploadFileAsClipWorker.Parameters.Input.UPLOAD_NAME to name, ), ).build() workManager.enqueue(request) return Id(request.id.toString()) } override fun getFileUploadStatusFlow( id: Id, ): Flow = workManager.getWorkInfoByIdFlow(UUID.fromString(id.value)).mapNotNull { it ?: error("No worker for this ID") }.mapNotNull { workInfo -> if (workInfo.state.isFinished) { when (workInfo.state) { WorkInfo.State.SUCCEEDED -> { val uploadId = workInfo.outputData.getString(UploadFileAsClipWorker.Parameters.Output.UPLOAD_ID) ?: return@mapNotNull UploadingFile.Failure( error = IllegalStateException( "${UploadFileAsClipWorker::class.simpleName} finished, " + "but ${UploadFileAsClipWorker.Parameters.Output.UPLOAD_ID} is null", ), ) val clipId = workInfo.outputData.getString(UploadFileAsClipWorker.Parameters.Output.CLIP_ID) ?: return@mapNotNull UploadingFile.Failure( error = IllegalStateException( "${UploadFileAsClipWorker::class.simpleName} finished, " + "but ${UploadFileAsClipWorker.Parameters.Output.CLIP_ID} is null", ), ) val name = workInfo.outputData.getString(UploadFileAsClipWorker.Parameters.Output.NAME) val thumbnail = workInfo.outputData.getString(UploadFileAsClipWorker.Parameters.Output.THUMBNAIL) ?.let { Url(it) } UploadingFile.Success( uploadId = Id(uploadId), clipId = Id(clipId), name = name ?: "", thumbnail = thumbnail, ) } WorkInfo.State.FAILED -> { val failMessage = workInfo.outputData.getString(UploadFileAsClipWorker.Parameters.Output.ERROR) ?: "${UploadFileAsClipWorker::class.simpleName} failed with no error message" UploadingFile.Failure( error = IllegalStateException(failMessage), ) } WorkInfo.State.CANCELLED -> { UploadingFile.Cancelled } else -> null // should be unreachable } } else { val step = workInfo.progress.getString(UploadFileAsClipWorker.Parameters.Progress.STEP)?.let { Result.runCatching { UploadFileAsClipWorker.Parameters.Progress.Steps.valueOf(it) }.getOrNull() } val uploadId = workInfo.progress.getString(UploadFileAsClipWorker.Parameters.Progress.UPLOAD_ID)?.let { Id(it) } val name = workInfo.progress.getString(UploadFileAsClipWorker.Parameters.Output.NAME) val thumbnail = workInfo.progress.getString(UploadFileAsClipWorker.Parameters.Output.THUMBNAIL)?.let(::Url) val percent = workInfo.progress.getFloat(UploadFileAsClipWorker.Parameters.Progress.PERCENT, -1f) .takeUnless { it == -1f } when (step) { UploadFileAsClipWorker.Parameters.Progress.Steps.Initializing -> UploadingFile.Initializing( name = name ?: "", thumbnail = thumbnail, ) UploadFileAsClipWorker.Parameters.Progress.Steps.Uploading -> UploadingFile.InProgress( uploadId = uploadId ?: return@mapNotNull null, step = UploadingFile.InProgress.Step.Uploading, progressInStep = percent, name = name ?: "", thumbnail = thumbnail, ) UploadFileAsClipWorker.Parameters.Progress.Steps.Finalizing -> UploadingFile.InProgress( uploadId = uploadId ?: return@mapNotNull null, step = UploadingFile.InProgress.Step.Finalize, progressInStep = percent, name = name ?: "", thumbnail = thumbnail, ) null -> null } } } override fun cancelUpload( id: Id, ) { workManager.cancelWorkById(UUID.fromString(id.value)) } }