Files
LlamaCasty/Services/RecordingFile.cs
T
gramps 536676f11a fix: millisecond segment in the recording filename so two instances can't clobber a take
The dev multi-instance use case (one instance screen-capturing the other for
marketing/training videos) exposed a real bug. BuildStartPath in
MainViewModel.Recording.cs builds the start name with no dedup:

    var name = RecordingFile.BuildStartName(DateTime.Now) + ".mp4";
    var path = Path.Combine(folder, name);

Only FinalizeRecordingAsync calls UniquePath. So two instances that start
recording in the same minute computed the identical path
(ty-20260927-1454-0000.mp4) and both ffmpeg processes wrote one file -- a
corrupt take, not an error message.

Creator's fix (2026-09-27): add a millisecond count to the filename timestamp.
New format is ty-<yyyyMMdd>-<HHmm>-<fff>-<hh2mm2>.mp4. Both the start name and
the final name derive from _recordStartTime, so the ffff segment survives the
rename-on-stop and the final name cannot collide with another same-minute take
either. No UniquePath call needed, which keeps the start path a pure function of
the timestamp and the existing collision suffix behaviour intact.

Tests: 2 new (the same-minute guard and the rename-survival guard), 5 existing
assertions updated for the format. RecordingSaveDialogTests' stem fixtures
updated to match. 369/369 pass, 0 warnings.

Note: the first test run reported 367/367 because only ytLive.csproj had been
built -- the stale test DLL never picked up the new tests. The real count is 369.
2026-09-27 15:37:46 -07:00

34 lines
1.6 KiB
C#

namespace ytLive.Services;
/// <summary>
/// Pure filename logic for local recordings (TASK 18). The user-requested naming
/// is <c>ty-yyyymmdd-hhmm-fff-hh2mm2.mp4</c>: <c>ty</c> is the brand marker, the first
/// <c>hhmm</c> is the hour+minute the recording STARTED (saved), <c>fff</c> is
/// milliseconds on the same start timestamp, and the last <c>hh2mm2</c> is the
/// hour+minute of the recording's LENGTH. Length is only known when recording ends,
/// so the file is created with a <c>0000</c> length and renamed on stop (see
/// <see cref="FinalizeFileName"/>).
/// <para>The <c>fff</c> exists so two instances started in the same minute cannot
/// compute the same path — the dev multi-instance use case (record one instance
/// with the other) would otherwise have both ffmpeg processes writing one file.
/// Both names derive from the same <c>_recordStartTime</c>, so the segment survives
/// the rename.</para>
/// </summary>
public static class RecordingFile
{
/// <summary>A start placeholder — the length is filled in on stop.</summary>
public const string StartLengthSuffix = "0000";
public static string BuildStartName(DateTime start)
=> $"ty-{start:yyyyMMdd}-{start:HHmm}-{start:fff}-{StartLengthSuffix}";
/// <summary>Final name with the real recording length (fractional hour+minute),
/// e.g. 75 minutes → <c>ty-20260829-1405-000-0115.mp4</c>.</summary>
public static string BuildFinalName(DateTime start, TimeSpan length)
{
var hh2 = (int)length.TotalHours;
var mm2 = length.Minutes;
return $"ty-{start:yyyyMMdd}-{start:HHmm}-{start:fff}-{hh2:00}{mm2:00}";
}
}