Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.0.7</Version>
<Version>10.0.9</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ public partial class HikVisionWebPlugin
/// </summary>
public bool IsRealPlaying { get; private set; }

/// <summary>
/// 获得 是否已经打开声音
/// </summary>
public bool IsOpenSound { get; private set; }

/// <summary>
/// 获得 是否开始录像
/// </summary>
public bool IsStartRecord { get; private set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand Down Expand Up @@ -164,6 +174,7 @@ public async Task Logout()
{
await InvokeVoidAsync("logout", Id);
}
IsStartRecord = false;
IsRealPlaying = false;
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsOpenSound state is not reset when logout occurs. When a user logs out, the sound state should also be reset to false, similar to how IsStartRecord and IsRealPlaying are reset.

Suggested change
IsRealPlaying = false;
IsRealPlaying = false;
IsOpenSound = false;

Copilot uses AI. Check for mistakes.
IsLogin = false;
await TriggerLogout();
Expand Down Expand Up @@ -221,6 +232,7 @@ public async Task StopRealPlay()
{
await InvokeVoidAsync("stopRealPlay", Id);
IsRealPlaying = false;
IsStartRecord = false;
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsOpenSound state should be reset when stopping real play. When stopping the real-time preview, the sound state should also be reset to false, similar to how IsStartRecord is reset.

Suggested change
IsStartRecord = false;
IsStartRecord = false;
IsOpenSound = false;

Copilot uses AI. Check for mistakes.
await TriggerStopRealPlay();
}
}
Expand Down Expand Up @@ -281,6 +293,7 @@ public async Task<bool> OpenSound()
{
var code = await InvokeAsync<int>("openSound", Id);
ret = code == 100;
IsOpenSound = true;
Comment on lines 294 to +296
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Local sound state is set to true even when the JS call fails.

ret correctly reflects whether openSound succeeded (code == 100), but IsOpenSound is always set to true, so it can become out of sync with the actual sound state when code != 100. Set IsOpenSound = ret (or only set it when code == 100) so it stays consistent with the real state.

Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsOpenSound property is set to true regardless of whether the operation actually succeeded. It should only be set to true when ret is true (i.e., when code == 100).

Suggested change
IsOpenSound = true;
IsOpenSound = ret;

Copilot uses AI. Check for mistakes.
}
return ret;
}
Expand All @@ -296,6 +309,7 @@ public async Task<bool> CloseSound()
{
var code = await InvokeAsync<int>("closeSound", Id);
ret = code == 100;
IsOpenSound = false;
Comment on lines 310 to +312
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Local sound state is set to false even if closeSound fails.

Because IsOpenSound is set to false unconditionally, callers may believe sound is closed even when closeSound fails. Align the flag with the actual result from code == 100 (e.g., set IsOpenSound = ret; or only update it when the call succeeds) so state remains accurate.

Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsOpenSound property is set to false regardless of whether the operation actually succeeded. It should only be set to false when ret is true (i.e., when code == 100).

Copilot uses AI. Check for mistakes.
}
return ret;
}
Expand All @@ -320,54 +334,30 @@ public async Task<bool> SetVolume(int value)
/// 抓图方法并且下载方法
/// </summary>
/// <returns></returns>
public async Task CapturePictureAndDownload()
public async Task<bool> CapturePictureAndDownload(string? fileName = null, CancellationToken token = default)
{
var ret = false;
if (IsLogin && IsRealPlaying)
{
await InvokeVoidAsync("capturePictureAndDownload", Id);
ret = await InvokeAsync<bool>("capturePictureAndDownload", token, Id, fileName);
}
return ret;
}

private TaskCompletionSource<IJSStreamReference?>? _captureTaskCompletionSource = null;

/// <summary>
/// 抓图方法返回 <see cref="IJSStreamReference"/> 实例
/// </summary>
/// <returns></returns>
Comment on lines 348 to 350
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML documentation comment is outdated. The method no longer returns an IJSStreamReference instance but now returns a boolean value indicating success or failure.

Suggested change
/// 抓图方法返回 <see cref="IJSStreamReference"/> 实例
/// </summary>
/// <returns></returns>
/// 抓图方法,返回操作是否成功
/// </summary>
/// <returns>表示抓图操作是否成功的布尔值。</returns>

Copilot uses AI. Check for mistakes.
public async Task<IJSStreamReference?> CapturePicture(CancellationToken token = default)
public async Task<bool> CapturePicture(string? fileName = null, CancellationToken token = default)
{
IJSStreamReference? ret = null;
var ret = false;
if (IsLogin && IsRealPlaying)
{
_captureTaskCompletionSource = new();

try
{
await InvokeVoidAsync("capturePicture", token, Id);
ret = await _captureTaskCompletionSource.Task;
}
catch (Exception ex)
{
_captureTaskCompletionSource.SetException(ex);
}
ret = await InvokeAsync<bool>("capturePicture", token, Id, fileName);
}
return ret;
}

/// <summary>
/// 抓图返回文件流方法 由 Javascript 调用
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
[JSInvokable]
public async Task TriggerReceivePictureStream(IJSStreamReference stream)
{
if (_captureTaskCompletionSource != null)
{
_captureTaskCompletionSource.SetResult(stream);
}
}

/// <summary>
/// 开始录像方法
/// </summary>
Expand All @@ -378,6 +368,7 @@ public async Task<bool> StartRecord()
if (IsLogin && IsRealPlaying)
{
ret = await InvokeAsync<bool>("startRecord", Id);
IsStartRecord = ret;
}
return ret;
}
Expand All @@ -392,6 +383,10 @@ public async Task<bool> StopRecord()
if (IsLogin && IsRealPlaying)
{
ret = await InvokeAsync<bool>("stopRecord", Id);
if (ret)
{
IsStartRecord = false;
}
}
return ret;
}
Expand Down
26 changes: 16 additions & 10 deletions src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,24 +409,24 @@ export async function setVolume(id, value) {
return code;
}

export async function capturePicture(id) {
export function capturePicture(id, szFileName) {
const vision = Data.get(id);
const { realPlaying } = vision;

if (realPlaying !== true) {
return "";
return false;
}

try {
const base64 = await WebVideoCtrl.I_CapturePicData();
if (base64) {
const bytes = base64ToArray(base64);
return DotNet.createJSStreamReference(bytes.buffer);
if (!szFileName) {
Comment on lines +412 to +421
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The capture filename here is extension-less, unlike the .jpg used in the download variant.

This uses a default of capture_* without an extension, whereas capturePictureAndDownload uses capture_*.jpg. To avoid confusion or downstream issues if consumers expect a .jpg, align the default naming here (e.g., also use .jpg) or otherwise keep the conventions consistent between the capture methods.

szFileName = `capture_${new Date().getTime()}`;
}
WebVideoCtrl.I_CapturePic(szFileName);
return true;
}
catch (ex) {
console.log(ex);
return null;
return false;
}
}

Expand All @@ -443,28 +443,34 @@ const base64ToArray = base64String => {
return bytes;
}

export async function capturePictureAndDownload(id) {
export async function capturePictureAndDownload(id, szFileName) {
const vision = Data.get(id);
const { realPlaying } = vision;

if (realPlaying !== true) {
return;
return false;
}

let ret = false;
try {
const base64 = await WebVideoCtrl.I_CapturePicData();
if (base64) {
if (!szFileName) {
szFileName = `capture_${new Date().getTime()}.jpg`
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the line. JavaScript requires semicolons to properly terminate statements.

Suggested change
szFileName = `capture_${new Date().getTime()}.jpg`
szFileName = `capture_${new Date().getTime()}.jpg`;

Copilot uses AI. Check for mistakes.
}
const anchorElement = document.createElement('a');
anchorElement.href = `data:image/jpg;base64,${base64}`;
anchorElement.download = `capture_${new Date().getTime()}.jpg`;
anchorElement.download = szFileName;
document.body.appendChild(anchorElement);
anchorElement.click();
document.body.removeChild(anchorElement);
ret = true;
}
}
catch (ex) {
console.log(ex);
}
return ret;
}

export async function startRecord(id) {
Expand Down
Loading