feat: add canonical beat pattern TYPE_BEATS, not synced to server

This commit is contained in:
2026-05-31 10:21:31 +02:00
parent b332c7b2f3
commit 12adfbc960
8 changed files with 268 additions and 56 deletions

View File

@@ -167,8 +167,9 @@ class LockstepViewModel(
}
/**
* Writes one JSON file under public Documents/Lockstep/{sessionFolder}/ for the track described
* by [event], records file metadata, and uploads when signed in. Skips when [beatTimesMs] is empty.
* Writes session annotation JSON under Documents/Lockstep/{sessionFolder}/ (synced when signed in)
* and canonical beat JSON under Documents/Lockstep/Beats/{playlist_name}/ (local file_metadata only).
* Skips when [beatTimesMs] is empty.
*/
fun persistBeatAnnotation(
playlistId: String,
@@ -184,9 +185,10 @@ class LockstepViewModel(
val pairing = pairingDao.findForTrack(playlistId, event.trackId)
val docId = BeatAnnotationStorage.mp3DocumentContentId(pairing?.localUri)
val contentId = docId.ifBlank { event.trackId }
val uri =
val appContext = getApplication<Application>()
val annotationUri =
BeatAnnotationStorage.writeAnnotationsFile(
context = getApplication(),
context = appContext,
sessionFolder = sessionFolder,
playlistDisplayName = playlistDisplayName,
trackQueueIndex0Based = event.queueIndex,
@@ -195,18 +197,66 @@ class LockstepViewModel(
artist = event.artist,
beatTimesMs = beatTimesMs,
) ?: return@launch
val entry =
FileMetadataEntity(
fileUri = uri.toString(),
trackId = event.trackId,
type = FileMetadataEntity.TYPE_ANNOTATION,
version = BuildConfig.VERSION_CODE,
)
val id = fileMetadataDao.insert(entry)
recordMetadataEntry(
fileUri = annotationUri.toString(),
trackId = event.trackId,
type = FileMetadataEntity.TYPE_ANNOTATION,
)
val beatsUri =
BeatAnnotationStorage.writeBeatsFile(
context = appContext,
playlistDisplayName = playlistDisplayName,
trackQueueIndex0Based = event.queueIndex,
contentId = contentId,
title = event.title,
artist = event.artist,
beatTimesMs = beatTimesMs,
) ?: return@launch
recordOrUpdateMetadataEntry(
fileUri = beatsUri.toString(),
trackId = event.trackId,
type = FileMetadataEntity.TYPE_BEATS,
)
}
}
private suspend fun recordMetadataEntry(
fileUri: String,
trackId: String,
type: String,
) {
val now = System.currentTimeMillis()
val beats = type == FileMetadataEntity.TYPE_BEATS
val entry =
FileMetadataEntity(
fileUri = fileUri,
trackId = trackId,
type = type,
version = BuildConfig.VERSION_CODE,
synced = beats,
lastSyncedAt = if (beats) now else null,
)
val id = fileMetadataDao.insert(entry)
if (!beats) {
syncMetadataEntry(entry.copy(id = id))
}
}
private suspend fun recordOrUpdateMetadataEntry(
fileUri: String,
trackId: String,
type: String,
) {
val version = BuildConfig.VERSION_CODE
val now = System.currentTimeMillis()
val existing = fileMetadataDao.findByTrackIdAndType(trackId, type)
if (existing != null) {
fileMetadataDao.updateFile(existing.id, fileUri, version, now)
} else {
recordMetadataEntry(fileUri, trackId, type)
}
}
/**
* Writes one JSON file under public Documents/Lockstep/{runSessionFolder}/ when a track finishes or is skipped.
* Skips when [samples] is empty or the track has no paired local URI.
@@ -333,7 +383,7 @@ class LockstepViewModel(
version = entry.version,
collection = collection,
)
fileMetadataDao.markSynced(entry.id)
fileMetadataDao.markSynced(entry.id, System.currentTimeMillis())
true
} catch (e: IOException) {
Log.w(METADATA_TAG, "metadata sync failed id=${entry.id}", e)