I properly need a pair of new eyes on my code.
(It’s unpolish, i know.)
The code was working earlier today. I could get it to download a zip file with a .mkv file in it.
However, then i started to make it more complete as a webapi soulution. And now for some reason it’s giving me an “UnknownCameraID”. All the time.
Even though the camera id is something that i lookup in the same code, from the camera name. And the camera is found and the Guid of the camera is correct.
What am i doing wrong here? When i log onto the XProtect Management Client i can see the investigations i am creating. It’s also listing the camera that i want to export from. but nothing gets exported.
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.IO.Compression;
using VideoOS.Mobile.Portable.MetaChannel;
using VideoOS.Mobile.Portable.MetaChannel.CommEntries;
using VideoOS.Mobile.Portable.Utilities;
using VideoOS.Mobile.Portable.VideoChannel.Params;
using VideoOS.Mobile.Portable.ViewGroupItem;
using VideoOS.Mobile.SDK.Portable.Server.Base.CommandResults;
using VideoOS.Mobile.SDK.Portable.Server.Base.Connection;
using VideoOS.Mobile.SDK.Portable.Server.ViewGroups;
namespace api.Controllers
{
[ApiController]
[Route(“[controller]”)]
public class VideoExportController : ControllerBase
{
private static bool Initialized = false;
private static Connection Connection { get; set; }
private static string Username { get; set; }
private static string Password { get; set; }
private static UserType UserType { get; set; } = UserType.Unknown;
private void OnConnectSuccess(ConnectResponse responseParams)
{
Connection.LogIn(Username, Password, ClientTypes.MobileClient, UserType, null,
OnLoginSuccess, OnFail);
}
private void OnFail(BaseCommandResponse responseParams)
{
Connection = null;
Initialized = true;
}
private static void OnLoginSuccess(BaseCommandResponse responseParams)
{
Connection.RunHeartBeat = true;
Initialized = true;
}
private async Task Connect()
{
string httpaddress = "surv-mob-01.thansen.dk";
uint httpport = 8082;
Username = "username";
Password = "password";
UserType = UserType.ActiveDirectory;
Connection = new Connection(ChannelTypes.HTTPSecure, httpaddress, httpport)
{
CommandsQueueing = CommandsQueueing.PerCommandTask
};
var result = await Connection.ConnectAsync(null, new TimeSpan(0, 0, 10));
var loginSettings = new LoginSettingsParams()
{
UserName = Username,
Password = Password,
LoginType = UserType.ActiveDirectory,
ClientType = ClientTypes.MobileClient
};
var loginResponse = await Connection.LogInAsync(loginSettings, null, new TimeSpan(0, 0, 10));
}
private async Task<string> PerformExport(string cameraname, DateTime startTime, DateTime endTime)
{
await Connect();
var cameraNames = new[] { cameraname };
var commandTimeout = TimeSpan.FromSeconds(120);
await Connection.Views.GetViewsAsync(new ViewParams(), commandTimeout);
var cameraIds = ViewGroupsHelper.GetAllCamerasView(Connection.Views, new ViewParams(), commandTimeout)
.Descendants(ViewItemType.Camera)
.Where(cam => cameraNames.Contains(cam.ItemName))
.Select(cam => cam.ItemId);
// Test if at least one camera with that name was found.
if (!cameraIds.Any())
{
return "Camera with Milestone Name '" + (cameraname ?? "null") + "' not found";
}
// Create investigation
var options = new InvestigationParams
{
Name = "WebAPI video Export",
StartTime = startTime,
EndTime = endTime,
State = InvestigationState.InProgress,
};
options.SetCameras(cameraIds);
var createInvestigationResponse = Connection.Investigations.CreateInvestigation(options, commandTimeout);
// always check any request's return code. If it's not OK, add a proper handling. For brevity, I'll only show this once
if (createInvestigationResponse.ErrorCode != ErrorCodes.Ok)
{
return createInvestigationResponse.ErrorMessage;
}
var investigationId = createInvestigationResponse.InvestigationId;
var sw = Stopwatch.StartNew();
while (sw.Elapsed < TimeSpan.FromMinutes(1) // longer investigations may take quite a lot of time to prepare
&& Connection.Investigations.GetInvestigation(investigationId, commandTimeout).Investigations.Single().Status != InvestigationSatus.Saved)
{
Thread.Sleep(1000);
}
if (investigationId == Guid.Empty)
return "Empty Investigation ID - Export not possible";
Connection.Investigations.UpdateInvestigation(
new InvestigationParams
{
InvestigationId = investigationId,
State = InvestigationState.Completed
}, commandTimeout);
var exportParams = new ExportParams
{
InvestigationId = investigationId,
CameraId = cameraIds.FirstOrDefault(),
Type = ExportType.Mkv,
};
var a = Connection.InvestigationExports.StartInvestigationExport(exportParams, commandTimeout);
var exportId = a.ExportId;
sw.Restart();
while (sw.Elapsed < TimeSpan.FromMinutes(1) // longer exports may take quite a lot of time to prepare
&& Connection.Exports.GetExport(exportId, commandTimeout).Exports.FirstOrDefault().State != (int)ExportState.Ready)
{
Thread.Sleep(1000);
}
if (exportId == Guid.Empty)
{
// return a.ErrorCode.ToString();
}
exportParams.ExportId = exportId;
var downloadLink = "https://surv-mob-01.thansen.dk:8082/" + Connection.Exports.CreateExportDownloadLink(exportParams, commandTimeout).ExportLink;
return downloadLink;
}
[HttpGet("export")]
public async Task<IActionResult> Export([FromQuery] DateTime FromDateTime, [FromQuery] DateTime ToDateTime, string CameraName)
{
var url = await PerformExport(CameraName, FromDateTime, ToDateTime);
if (url == null || !url.Contains("/XProtectMobile/Export/"))
return BadRequest("Ikke muligt at eksportere Video - " + url);
var handler = new HttpClientHandler
{
UseDefaultCredentials = true
};
var client = new HttpClient(handler);
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
if (!response.IsSuccessStatusCode)
return StatusCode((int)response.StatusCode);
var zipStream = await response.Content.ReadAsStreamAsync();
using var archive = new ZipArchive(zipStream);
var mkvEntry = archive.Entries.First(x => x.Name.EndsWith(".mkv"));
var mkvStream = mkvEntry.Open();
return File(mkvStream, "video/x-matroska", "video.mkv");
}
}
}
