Запись: короткий звонок и «Тривалість» — по присутствию участников, а не по длине аудио

This commit is contained in:
crm-calls agent
2026-10-15 07:01:55 +00:00
parent 007d6d7821
commit 78f91952e8
2 changed files with 19 additions and 3 deletions

View File

@@ -190,6 +190,8 @@ public sealed class ProcessorTests : IDisposable
var c = _calls.Create("ws-room", "wsteam0000001");
_calls.RecordingStarted(c.Id, c.Id);
_calls.Seen(c.Id, [new("Анна", false), new("Іван", true)]);
_time.Advance(TimeSpan.FromSeconds(duration)); // звонок шёл duration секунд
_calls.Seen(c.Id, [new("Анна", false), new("Іван", true)]);
_calls.Ended(c.Id, _calls.Now, hadRecording: true);
var dir = Path.Combine(_root, "rec", c.Id);
Directory.CreateDirectory(Path.Combine(dir, "parts"));
@@ -216,6 +218,7 @@ public sealed class ProcessorTests : IDisposable
Assert.False(Directory.Exists(dir)); // проверка 3: файлов не осталось
var up = Assert.Single(Uploads);
Assert.Matches(@"^calls/2026/10/[0-9a-f]{24}/dzvinok-2026-10-15-1400\.mp3$", up);
Assert.Contains("1 хв 30 с", JsonDocument.Parse(Pages[0]).RootElement.GetProperty("markdown").GetString());
Assert.StartsWith("https://sb.test/storage/v1/object/public/files/calls/2026/10/", row.AudioUrl);
Assert.Equal(["Дзвінки"], Folders); // папку создали
var page = JsonDocument.Parse(Assert.Single(Pages)).RootElement;
@@ -229,6 +232,17 @@ public sealed class ProcessorTests : IDisposable
Assert.Contains(row.AudioUrl!, md);
}
[Fact]
public async Task ShortCallByPresence_SkippedEvenIfAudioLonger()
{
// звонок 50 с, но запись 70 с (хвост после ухода всех) — по звонку он короткий
var (call, dir) = EndedCall(duration: 50);
File.WriteAllText(Path.Combine(dir, "meta.json"), File.ReadAllText(Path.Combine(dir, "meta.json")).Replace("\"duration\":50", "\"duration\":70"));
await Proc(Http(), Opt()).RunOnceAsync(default);
Assert.Equal("skipped", _calls.Get(call.Id)!.Status);
Assert.False(Directory.Exists(dir));
}
[Fact]
public async Task ShortCall_Skipped_NoPage_FilesDeleted()
{

View File

@@ -64,11 +64,13 @@ public sealed class RecordingProcessor(
return;
}
var meta = JsonDocument.Parse(await File.ReadAllTextAsync(Path.Combine(dir, "meta.json"), ct)).RootElement;
var duration = meta.GetProperty("duration").GetDouble();
var audioSeconds = meta.GetProperty("duration").GetDouble();
// Длительность звонка — по присутствию участников (запись идёт ещё EmptyGrace после ухода всех).
double duration = call.EndedAt is { } end && end > call.StartedAt ? end - call.StartedAt : audioSeconds;
var audioFile = meta.GetProperty("audio").GetString()!;
var mime = meta.GetProperty("mime").GetString()!;
if (meta.TryGetProperty("quality", out var q))
log.LogInformation("Дзвінок {C}: запис {D:0} с, {B} КБ, якість: {Q}", call.Id, duration,
log.LogInformation("Дзвінок {C}: тривалість {D:0} с, запис {A:0} с, {B} КБ, якість: {Q}", call.Id, duration, audioSeconds,
meta.GetProperty("bytes").GetInt64() / 1024, q.GetRawText());
// Короткий звонок: без страницы, файлы удаляем.
@@ -100,7 +102,7 @@ public sealed class RecordingProcessor(
var parts = meta.GetProperty("parts").EnumerateArray()
.Select(p => new AudioPart(p.GetProperty("file").GetString()!, p.GetProperty("duration").GetDouble(), p.GetProperty("bytes").GetInt64()))
.ToList();
if (parts.Count == 0) parts.Add(new AudioPart(audioFile, duration, meta.GetProperty("bytes").GetInt64()));
if (parts.Count == 0) parts.Add(new AudioPart(audioFile, audioSeconds, meta.GetProperty("bytes").GetInt64()));
transcript = await whisper.TranscribeAsync(dir, parts, mime, ct);
await File.WriteAllTextAsync(tPath + ".tmp", JsonSerializer.Serialize(transcript), ct);
File.Move(tPath + ".tmp", tPath, overwrite: true);