fixes to time conversion and play head ux + improvements to file scanner

This commit is contained in:
2026-03-10 22:07:04 -04:00
parent 951e285f81
commit b7c00323e8
7 changed files with 141 additions and 45 deletions

View File

@@ -21,8 +21,8 @@ public class AZKiRpcService(MediaService mediaService) : RPC.AZKi.AZKiBase
public override async Task<PlaybackInfo> GetMediaPlayback(MediaPlaybackRequest request, ServerCallContext context)
{
var from = request.Date.ToDateTime().Date;
var to = request.Date.ToDateTime().Date.AddDays(1);
var from = request.Date.ToDateTime().ToLocalTime().Date.AddDays(1);
var to = request.Date.ToDateTime().ToLocalTime().Date.AddDays(2);
var items = await mediaService.GetEntriesInRangeAsync(Models.MediaType.All, from, to);
var channels = items.GroupBy(i => i.CameraId).Select(c =>
{
@@ -38,7 +38,7 @@ public class AZKiRpcService(MediaService mediaService) : RPC.AZKi.AZKiBase
});
var playback = new PlaybackInfo
{
Date = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(from),
Date = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(from.ToUniversalTime()),
};
playback.Channels.AddRange(channels.OrderBy(c => c.CameraId));
return playback;

View File

@@ -12,6 +12,7 @@ using SixLabors.ImageSharp;
using System.Collections.Frozen;
using System.IO;
using System.Threading;
namespace AZKiServer.Services;
@@ -61,8 +62,7 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
var upToDateFiles = existingFiles.Where(e => e.Value == MediaEntry.CUR_VERSION)
.Select(e => e.Key)
.ToFrozenSet();
var entriesToDelete = existingFiles.Keys.Except(filesRelative).ToArray();
await mediaService.DeleteEntriesBulkAsync(files, cancellationToken);
//await DeleteEntriesWithoutFilesAsync(existingFiles, filesRelative, cancellationToken);
var filesToProcess = filesRelative.Where(f => !upToDateFiles.Contains(f)).ToArray();
var total = 0;
var prog = 0;
@@ -70,7 +70,7 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
{
prog += chunk.Length;
total += await ScanFileChunkAsync(path, chunk, existingFiles, cancellationToken);
logger.LogInformation("Added {updated} of {count} [{percentage}%]", total, filesToProcess.Length, Math.Round(((float)prog/ filesToProcess.Length) * 100));
logger.LogInformation("Added {updated} of {count} [{percentage}%]", total, filesToProcess.Length, Math.Round(((float)prog / filesToProcess.Length) * 100));
}
}
catch (Exception ex)
@@ -79,18 +79,28 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
}
}
private async Task DeleteEntriesWithoutFilesAsync(FrozenDictionary<string, int> existingFiles, string[] filesRelative, CancellationToken cancellationToken = default)
{
var entriesToDelete = existingFiles.Keys.Except(filesRelative).ToArray();
if (entriesToDelete.Length == 0)
return;
logger.LogInformation("Deleting {count} entires that no longer exist on file system.", entriesToDelete.Length);
await mediaService.DeleteEntriesBulkAsync(entriesToDelete, cancellationToken);
logger.LogInformation("{count} entries deleted.", entriesToDelete.Length);
}
private async Task<int> ScanFileChunkAsync(string path, IEnumerable<string> files, FrozenDictionary<string, int> existingFiles, CancellationToken cancellationToken = default)
{
var entries = new List<MediaEntry>();
var upgradeEntries = new List<MediaEntry>();
foreach (var filePath in files)
foreach (var relativePath in files)
{
if (cancellationToken.IsCancellationRequested)
break;
var relativePath = Path.GetRelativePath(path, filePath);
if (relativePath[0] == '.') //Ignore hidden folders
continue;
var absolutePath = Path.Combine(path, relativePath);
var isUpgrade = false;
if (existingFiles.TryGetValue(relativePath, out var version))
{
@@ -98,10 +108,10 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
continue;
isUpgrade = true;
}
var metadata = ReadMetadata(filePath);
var metadata = ReadMetadata(absolutePath);
if (metadata.HasError)
{
logger.LogError(metadata.Error.GetException(), $"Failed to get metadata for file: {filePath}");
logger.LogError(metadata.Error.GetException(), "Failed to get metadata for file: {filePath}", relativePath);
continue;
}
var entry = MediaEntry.Parse(relativePath, metadata);
@@ -137,7 +147,7 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
{
".jpg" or ".png" or ".jpeg" => ReadImageMetadata(filePath),
".mp4" => ReadVideoMetadata(filePath),
_ => throw new NotSupportedException($"Files of type {ext} are not supported")
_ => new Error($"Files of type {ext} are not supported")
};
}
@@ -164,7 +174,7 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
return new Error($"Could not find a primirary video stream in file.");
return new MediaMetadata((int)info.Duration.TotalSeconds, info.PrimaryVideoStream.Height, info.PrimaryVideoStream.Width);
}
catch(Exception ex)
catch (Exception ex)
{
return ex;
}

View File

@@ -37,7 +37,10 @@ public class MediaService(IMongoDatabase db)
/// <returns></returns>
public async Task DeleteEntriesBulkAsync(IEnumerable<string> files, CancellationToken cancellationToken = default)
{
await _entries.DeleteManyAsync(e => files.Contains(e.Filepath), cancellationToken);
foreach (var file in files)
{
await _entries.DeleteOneAsync(e => e.Filepath == file, cancellationToken);
}
}
public async Task<List<MediaEntry>> GetEntriesInRangeAsync(MediaType mediaType, DateTime from, DateTime to, CancellationToken cancellationToken = default)