feat: Add lock / unlock method for interval files

Signed-off-by: Myzel394 <50424412+Myzel394@users.noreply.github.com>
This commit is contained in:
Myzel394 2024-03-16 18:43:26 +01:00
parent 671c8da56a
commit 6627289666
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
5 changed files with 42 additions and 22 deletions

View File

@ -3,28 +3,26 @@ package app.myzel394.alibi.helpers
import android.Manifest
import android.content.ContentUris
import android.content.ContentValues
import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.provider.MediaStore.Video.Media
import androidx.documentfile.provider.DocumentFile
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import com.arthenica.ffmpegkit.FFmpegKitConfig
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
import app.myzel394.alibi.ui.utils.PermissionHelper
import com.arthenica.ffmpegkit.FFprobeKit
import com.arthenica.ffmpegkit.FFmpegKitConfig
import kotlinx.coroutines.CompletableDeferred
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.reflect.KFunction4
abstract class BatchesFolder(
@ -197,7 +195,6 @@ abstract class BatchesFolder(
createNewFile()
}
fun checkIfOutputAlreadyExists(
date: LocalDateTime,
extension: String
@ -388,12 +385,12 @@ abstract class BatchesFolder(
}
}
fun deleteOldRecordings(earliestCounter: Long) {
fun deleteRecordings(range: LongRange) {
when (type) {
BatchType.INTERNAL -> getInternalFolder().listFiles()?.forEach {
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
if (fileCounter < earliestCounter) {
if (fileCounter in range) {
it.delete()
}
}
@ -401,7 +398,7 @@ abstract class BatchesFolder(
BatchType.CUSTOM -> getCustomDefinedFolder().listFiles().forEach {
val fileCounter = it.name?.substringBeforeLast(".")?.toIntOrNull() ?: return@forEach
if (fileCounter < earliestCounter) {
if (fileCounter in range) {
it.delete()
}
}
@ -411,7 +408,7 @@ abstract class BatchesFolder(
val deletableNames = mutableListOf<String>()
queryMediaContent { rawName, counter, _, _ ->
if (counter < earliestCounter) {
if (counter in range) {
deletableNames.add(rawName)
}
}
@ -428,7 +425,7 @@ abstract class BatchesFolder(
it.nameWithoutExtension.substring(mediaPrefix.length).toIntOrNull()
?: return@forEach
if (fileCounter < earliestCounter) {
if (fileCounter in range) {
it.delete()
}
}

View File

@ -11,6 +11,9 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
protected var counter = 0L
private set
// Tracks the index of the currently locked file
private var lockedIndex: Long? = null
lateinit var settings: AppSettings
private lateinit var cycleTimer: ScheduledExecutorService
@ -21,6 +24,23 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
abstract fun getRecordingInformation(): I
// When saving the recording, the files should be locked.
// This prevents the service from deleting the currently available files, so that
// they can be safely used to save the recording.
// Once finished, make sure to unlock the files using `unlockFiles`.
fun lockFiles() {
lockedIndex = counter
}
// Unlocks and deletes the files that were locked using `lockFiles`.
fun unlockFiles(cleanupFiles: Boolean = false) {
if (cleanupFiles) {
batchesFolder.deleteRecordings(0..lockedIndex!!)
}
lockedIndex = null
}
// Make overrideable
open fun startNewCycle() {
counter += 1
@ -72,12 +92,12 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
private fun deleteOldRecordings() {
val timeMultiplier = settings.maxDuration / settings.intervalDuration
val earliestCounter = counter - timeMultiplier
val earliestCounter = Math.max(counter - timeMultiplier, lockedIndex ?: 0)
if (earliestCounter <= 0) {
return
}
batchesFolder.deleteOldRecordings(earliestCounter)
batchesFolder.deleteRecordings(0..earliestCounter)
}
}

View File

@ -139,7 +139,7 @@ class VideoRecorderService :
if (_cameraAvailableListener.isCompleted) {
action()
} else {
// Race condition of `startNewCycle` being called before `invpkeOnCompletion`
// Race condition of `startNewCycle` being called before `invokeOnCompletion`
// has been called can be ignored, as the camera usually opens within 5 seconds
// and the interval can't be set shorter than 10 seconds.
_cameraAvailableListener.invokeOnCompletion {

View File

@ -129,15 +129,17 @@ fun RecorderEventsHandler(
}
}
suspend fun saveRecording(recorder: RecorderModel) {
suspend fun saveRecording(recorder: RecorderModel): Thread {
isProcessing = true
// Give the user some time to see the processing dialog
delay(100)
thread {
return thread {
runBlocking {
try {
recorder.recorderService?.lockFiles()
val recording =
// When new recording created
recorder.recorderService?.getRecordingInformation()
@ -215,6 +217,7 @@ fun RecorderEventsHandler(
} catch (error: Exception) {
Log.getStackTraceString(error)
} finally {
recorder.recorderService?.unlockFiles()
isProcessing = false
}
}
@ -230,7 +233,7 @@ fun RecorderEventsHandler(
// instead of hoping that the coroutine from where this will be called will be alive
// until the end of the saving process
scope.launch {
saveRecording(audioRecorder as RecorderModel)
saveRecording(audioRecorder as RecorderModel).join()
}
}
audioRecorder.onRecordingStart = {
@ -273,7 +276,7 @@ fun RecorderEventsHandler(
// instead of hoping that the coroutine from where this will be called will be alive
// until the end of the saving process
scope.launch {
saveRecording(videoRecorder as RecorderModel)
saveRecording(videoRecorder as RecorderModel).join()
}
}
videoRecorder.onRecordingStart = {

View File

@ -227,7 +227,7 @@ fun _PrimitiveControls(videoRecorder: VideoRecorderModel) {
it.saveLastRecording(videoRecorder as RecorderModel)
}
videoRecorder.onRecordingSave()
videoRecorder.onRecordingSave().join()
runCatching {
videoRecorder.destroyService(context)