package com.dmc.callcrm.sync import android.content.Context import android.util.Log import com.dmc.callcrm.data.Prefs import com.dmc.callcrm.data.RecordingScanner import com.dmc.callcrm.network.ApiClient import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody /** Scans for new recordings and uploads each, tagged with the client number. */ object RecordingUploader { private const val TAG = "RecordingUploader" /** Upload one known audio file with explicit metadata. Marks it uploaded on success. */ suspend fun uploadOne( ctx: Context, file: java.io.File, clientNumber: String, callDateMs: Long, durationSec: Int, callType: String, contactName: String?, deviceCallId: String? ): Boolean { val prefs = Prefs(ctx) if (!prefs.isRegistered || !file.isFile || file.length() < 2048) return false return try { val mime = when (file.extension.lowercase()) { "mp3" -> "audio/mpeg"; "amr" -> "audio/amr"; "wav" -> "audio/wav" "ogg" -> "audio/ogg"; "aac" -> "audio/aac"; "3gp" -> "audio/3gpp" else -> "audio/mp4" } val filePart = MultipartBody.Part.createFormData( "file", file.name, file.asRequestBody(mime.toMediaTypeOrNull()) ) fun t(v: String): RequestBody = v.toRequestBody("text/plain".toMediaTypeOrNull()) val resp = ApiClient.service.uploadRecording( bearer = prefs.bearer(), file = filePart, apiToken = t(prefs.apiToken ?: ""), clientNumber = t(clientNumber), callDate = t(callDateMs.toString()), duration = t(durationSec.toString()), callType = t(callType), contactName = t(contactName ?: ""), deviceCallId = t(deviceCallId ?: "") ) if (resp.isSuccessful && resp.body()?.ok == true) { prefs.markUploaded(file.absolutePath) Log.d(TAG, "uploaded ${file.name} for $clientNumber") true } else { Log.w(TAG, "upload failed ${resp.code()} for ${file.name}"); false } } catch (e: Exception) { Log.e(TAG, "upload error ${file.name}", e); false } } suspend fun run(ctx: Context): Boolean { val prefs = Prefs(ctx) if (!prefs.isRegistered) return false val scanner = RecordingScanner(ctx) val uploaded = prefs.uploadedRecordings val recs = scanner.scan(prefs.lastRecordingScan, uploaded) if (recs.isEmpty()) return true var newestSeen = prefs.lastRecordingScan var allOk = true for (r in recs) { try { val mime = when (r.file.extension.lowercase()) { "mp3" -> "audio/mpeg"; "amr" -> "audio/amr"; "wav" -> "audio/wav" "ogg" -> "audio/ogg"; "aac" -> "audio/aac"; "3gp" -> "audio/3gpp" else -> "audio/mp4" } val filePart = MultipartBody.Part.createFormData( "file", r.file.name, r.file.asRequestBody(mime.toMediaTypeOrNull()) ) fun text(v: String): RequestBody = v.toRequestBody("text/plain".toMediaTypeOrNull()) val resp = ApiClient.service.uploadRecording( bearer = prefs.bearer(), file = filePart, apiToken = text(prefs.apiToken ?: ""), clientNumber = text(r.clientNumber), callDate = text(r.callDateMs.toString()), duration = text(r.durationSec.toString()), callType = text(r.callType), contactName = text(r.contactName ?: ""), deviceCallId = text(r.deviceCallId ?: "") ) if (resp.isSuccessful && resp.body()?.ok == true) { prefs.markUploaded(r.file.absolutePath) if (r.file.lastModified() > newestSeen) newestSeen = r.file.lastModified() Log.d(TAG, "uploaded ${r.file.name} for ${r.clientNumber}") } else { allOk = false Log.w(TAG, "upload failed ${resp.code()} for ${r.file.name}") } } catch (e: Exception) { allOk = false Log.e(TAG, "upload error for ${r.file.name}", e) } } // Advance the scan cursor only when everything in this pass succeeded, // so failures get retried next run. if (allOk) prefs.lastRecordingScan = newestSeen return allOk } }