Skip to content

Commit

Permalink
Merge pull request #108 from Myzel394/fix-105-custom-filename
Browse files Browse the repository at this point in the history
Add custom filename option
  • Loading branch information
Myzel394 authored Aug 21, 2024
2 parents 061ed8b + 8240a1c commit a88507a
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 75 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release-app-google-play.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ jobs:
track: production
status: inProgress
inAppUpdatePriority: 2
userFraction: 0.33
userFraction: 0.2
12 changes: 6 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ android {
applicationId "app.myzel394.alibi"
minSdk 24
targetSdk 34
versionCode 14
versionName "0.5.1"
versionCode 15
versionName "0.5.2"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -94,9 +94,9 @@ android {
dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.3'
implementation 'androidx.activity:activity-compose:1.9.0'
implementation 'androidx.activity:activity-ktx:1.9.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4'
implementation 'androidx.activity:activity-compose:1.9.1'
implementation 'androidx.activity:activity-ktx:1.9.1'
implementation platform('androidx.compose:compose-bom:2024.06.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
Expand All @@ -105,7 +105,7 @@ dependencies {
implementation "androidx.compose.material:material-icons-extended:1.6.8"
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.lifecycle:lifecycle-service:2.8.3'
implementation 'androidx.lifecycle:lifecycle-service:2.8.4'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
Expand Down
34 changes: 32 additions & 2 deletions app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ data class AppSettings(
val theme: Theme = Theme.SYSTEM,
val lastRecording: RecordingInformation? = null,

val filenameFormat: FilenameFormat = FilenameFormat.DATETIME_RELATIVE_START,

/// Recording information
// 30 minutes
val maxDuration: Long = 15 * 60 * 1000L,
Expand Down Expand Up @@ -67,6 +69,10 @@ data class AppSettings(
return copy(lastRecording = lastRecording)
}

fun setFilenameFormat(filenameFormat: FilenameFormat): AppSettings {
return copy(filenameFormat = filenameFormat)
}

fun setMaxDuration(duration: Long): AppSettings {
if (duration < 60 * 1000L || duration > 10 * 24 * 60 * 60 * 1000L) {
throw Exception("Max duration must be between 1 minute and 10 days")
Expand Down Expand Up @@ -124,14 +130,20 @@ data class AppSettings(
))
}

fun exportToString(): String {
return Json.encodeToString(serializer(), this)
}

enum class Theme {
SYSTEM,
LIGHT,
DARK,
}

fun exportToString(): String {
return Json.encodeToString(serializer(), this)
enum class FilenameFormat {
DATETIME_ABSOLUTE_START,
DATETIME_RELATIVE_START,
DATETIME_NOW,
}

companion object {
Expand All @@ -151,6 +163,7 @@ data class RecordingInformation(
val folderPath: String,
@Serializable(with = LocalDateTimeSerializer::class)
val recordingStart: LocalDateTime,
val batchesAmount: Int,
val maxDuration: Long,
val intervalDuration: Long,
val fileExtension: String,
Expand All @@ -165,6 +178,23 @@ data class RecordingInformation(
.hasRecordingsAvailable()
}

fun getStartDateForFilename(filenameFormat: AppSettings.FilenameFormat): LocalDateTime {
return when (filenameFormat) {
AppSettings.FilenameFormat.DATETIME_ABSOLUTE_START -> recordingStart
AppSettings.FilenameFormat.DATETIME_RELATIVE_START -> LocalDateTime.now().minusSeconds(
getFullDuration() / 1000
)

AppSettings.FilenameFormat.DATETIME_NOW -> LocalDateTime.now()
}
}

fun getFullDuration(): Long {
// This is not accurate, since the last batch may be shorter than the others
// but it's good enough
return intervalDuration * batchesAmount - (intervalDuration * 0.5).toLong()
}

enum class Type {
AUDIO,
VIDEO,
Expand Down
67 changes: 25 additions & 42 deletions app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.annotation.RequiresApi
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.RecordingInformation
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
Expand Down Expand Up @@ -249,19 +250,23 @@ abstract class BatchesFolder(
abstract fun cleanup()

suspend fun concatenate(
recordingStart: LocalDateTime,
extension: String,
recording: RecordingInformation,
filenameFormat: AppSettings.FilenameFormat,
disableCache: Boolean? = null,
onNextParameterTry: (String) -> Unit = {},
durationPerBatchInMilliseconds: Long = 0,
onProgress: (Float?) -> Unit = {},
): String {
val disableCache = disableCache ?: (type != BatchType.INTERNAL)
val date = recording.getStartDateForFilename(filenameFormat)

if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) {
if (!disableCache && checkIfOutputAlreadyExists(
recording.recordingStart,
recording.fileExtension
)
) {
return getOutputFileForFFmpeg(
date = recordingStart,
extension = extension,
date = recording.recordingStart,
extension = recording.fileExtension,
)
}

Expand All @@ -271,34 +276,12 @@ abstract class BatchesFolder(
onProgress(null)

try {
val fullTime = recording.getFullDuration().toFloat();
val filePaths = getBatchesForFFmpeg()

// Casting here to float so it doesn't need to redo it on every progress update
var fullTime: Float? = null

runCatching {
// `fullTime` is not accurate as the last batch might be shorter,
// but it's good enough for the progress bar

// Using the code below results in a nasty bug:
// since we use ffmpeg to extract the duration, the saf parameter is already
// "used up" and we can't use it again for the actual concatenation
// Since an accurate progress bar is less important than speed,
// we currently don't use this code
/*
val lastBatchTime = (FFprobeKit.execute(
"-i ${filePaths.last()} -show_entries format=duration -v quiet -of csv=\"p=0\"",
).output.toFloat() * 1000).toLong()
fullTime =
((durationPerBatchInMilliseconds * (filePaths.size - 1)) + lastBatchTime).toFloat()
*/
// We use an approximation for the duration of the batches
fullTime = (durationPerBatchInMilliseconds * filePaths.size).toFloat()
}

val outputFile = getOutputFileForFFmpeg(
date = recordingStart,
extension = extension,
date = date,
extension = recording.fileExtension,
)

concatenationFunction(
Expand All @@ -308,11 +291,7 @@ abstract class BatchesFolder(
) { time ->
// The progressbar for the conversion is calculated based on the
// current time of the conversion and the total time of the batches.
if (fullTime != null) {
onProgress(time / fullTime!!)
} else {
onProgress(null)
}
onProgress(time / fullTime)
}.await()
return outputFile
} catch (e: MediaConverter.FFmpegException) {
Expand Down Expand Up @@ -607,19 +586,23 @@ abstract class BatchesFolder(
}

fun canAccessFolder(context: Context, uri: Uri): Boolean {
// This always returns false for some reason, let's just assume it's true
return true
/*
return try {
// Create temp file
val tempFile = DocumentFile.fromSingleUri(context, uri)!!.createFile(
"application/octet-stream",
"temp"
)!!
tempFile.delete()
val docFile = DocumentFile.fromSingleUri(context, uri)!!
true
return docFile.canWrite().also {
println("Can write? ${it}")
} && docFile.canRead().also {
println("Can read? ${it}")
}
} catch (error: RuntimeException) {
error.printStackTrace()
false
}
*/
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import app.myzel394.alibi.db.RecordingInformation
import app.myzel394.alibi.enums.RecorderState
import app.myzel394.alibi.helpers.AudioBatchesFolder
import app.myzel394.alibi.helpers.BatchesFolder
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
import app.myzel394.alibi.ui.utils.MicrophoneInfo
import java.lang.IllegalStateException

class AudioRecorderService :
IntervalRecorderService<RecordingInformation, AudioBatchesFolder>() {
Expand Down Expand Up @@ -304,12 +302,14 @@ class AudioRecorderService :
}

// ==== Settings ====
override fun getRecordingInformation() = RecordingInformation(
folderPath = batchesFolder.exportFolderForSettings(),
recordingStart = recordingStart,
maxDuration = settings.maxDuration,
fileExtension = settings.audioRecorderSettings.fileExtension,
intervalDuration = settings.intervalDuration,
type = RecordingInformation.Type.AUDIO,
)
override fun getRecordingInformation() =
RecordingInformation(
folderPath = batchesFolder.exportFolderForSettings(),
recordingStart = recordingStart,
maxDuration = settings.maxDuration,
batchesAmount = batchesFolder.getBatchesForFFmpeg().size,
fileExtension = settings.audioRecorderSettings.fileExtension,
intervalDuration = settings.intervalDuration,
type = RecordingInformation.Type.AUDIO,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,16 @@ class VideoRecorderService :
this
}

override fun getRecordingInformation(): RecordingInformation = RecordingInformation(
folderPath = batchesFolder.exportFolderForSettings(),
recordingStart = recordingStart,
maxDuration = settings.maxDuration,
fileExtension = settings.videoRecorderSettings.fileExtension,
intervalDuration = settings.intervalDuration,
type = RecordingInformation.Type.VIDEO,
)
override fun getRecordingInformation() =
RecordingInformation(
folderPath = batchesFolder.exportFolderForSettings(),
recordingStart = recordingStart,
maxDuration = settings.maxDuration,
batchesAmount = batchesFolder.getBatchesForFFmpeg().size,
fileExtension = settings.videoRecorderSettings.fileExtension,
intervalDuration = settings.intervalDuration,
type = RecordingInformation.Type.VIDEO,
)

companion object {
const val CAMERA_CLOSE_TIMEOUT = 20000L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ fun RecorderEventsHandler(
// When recording is loaded from lastRecording
?: settings.lastRecording
?: throw Exception("No recording information available")

val batchesFolder = when (recorder.javaClass) {
AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder(
recording.folderPath,
Expand All @@ -177,9 +178,8 @@ fun RecorderEventsHandler(
}

batchesFolder.concatenate(
recording.recordingStart,
recording.fileExtension,
durationPerBatchInMilliseconds = settings.intervalDuration,
recording,
filenameFormat = settings.filenameFormat,
onProgress = { percentage ->
processingProgress = percentage
}
Expand Down
Loading

0 comments on commit a88507a

Please sign in to comment.