mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Added session recording. Added CapsLock key mapping.
This commit is contained in:
parent
ea8281e787
commit
b658264229
4
.gitignore
vendored
4
.gitignore
vendored
@ -265,4 +265,6 @@ __pycache__/
|
||||
/Remotely_Server/wwwroot/Downloads/*.appimage
|
||||
/Remotely_Server/wwwroot/Downloads/CurrentAgentVersion.txt
|
||||
/Remotely_Server/Server.db
|
||||
/Remotely_Agent/Resources/*
|
||||
/Remotely_Agent/Resources/*
|
||||
/Remotely_Server/Recordings/*
|
||||
/Remotely_Server/ffmpeg.exe
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_Library.Models
|
||||
{
|
||||
public class AttendedSessionInfo
|
||||
{
|
||||
public string SignalRConnectionID { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@ -99,7 +99,7 @@ namespace Remotely_ScreenCast.Capture
|
||||
// continue;
|
||||
//}
|
||||
|
||||
while (viewer.PendingFrames > 30)
|
||||
while (viewer.PendingFrames > 10)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
@ -51,10 +51,11 @@ namespace Remotely_ScreenCast
|
||||
|
||||
public static async Task HandleConnection()
|
||||
{
|
||||
OutgoingMessages.SendDeviceInfo(ServiceID, Environment.MachineName).Wait();
|
||||
|
||||
|
||||
if (Mode == AppMode.Unattended)
|
||||
{
|
||||
OutgoingMessages.SendServiceID(ServiceID).Wait();
|
||||
|
||||
var desktopName = Win32Interop.GetCurrentDesktop();
|
||||
if (desktopName.ToLower() != CurrentDesktopName.ToLower())
|
||||
{
|
||||
|
||||
@ -47,9 +47,9 @@ namespace Remotely_ScreenCast.Sockets
|
||||
await Connection.SendAsync("NotifyViewersRelaunchedScreenCasterReady", viewerIDs);
|
||||
}
|
||||
|
||||
internal async Task SendServiceID(string serviceID)
|
||||
internal async Task SendDeviceInfo(string serviceID, string machineName)
|
||||
{
|
||||
await Connection.SendAsync("ReceiveServiceID", serviceID);
|
||||
await Connection.SendAsync("ReceiveDeviceInfo", serviceID, machineName);
|
||||
}
|
||||
|
||||
internal async Task SendConnectionFailedToViewers(List<string> viewerIDs)
|
||||
|
||||
@ -278,6 +278,12 @@ namespace Win32
|
||||
case "Backspace":
|
||||
keyCode = (short)VirtualKey.BACK;
|
||||
break;
|
||||
case "Tab":
|
||||
keyCode = (short)VirtualKey.TAB;
|
||||
break;
|
||||
case "CapsLock":
|
||||
keyCode = (short)VirtualKey.CAPITAL;
|
||||
break;
|
||||
case "Delete":
|
||||
keyCode = (short)VirtualKey.DELETE;
|
||||
break;
|
||||
|
||||
30
Remotely_Server/Models/RemoteControlFrame.cs
Normal file
30
Remotely_Server/Models/RemoteControlFrame.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Remotely_Server.Models
|
||||
{
|
||||
public class RemoteControlFrame
|
||||
{
|
||||
|
||||
public RemoteControlFrame(byte[] frameBytes, int left, int top, int width, int height, string viewerID, string machineName, DateTime startTime)
|
||||
{
|
||||
this.FrameBytes = frameBytes;
|
||||
this.Left = left;
|
||||
this.Top = top;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this.ViewerID = viewerID;
|
||||
this.MachineName = machineName;
|
||||
this.StartTime = startTime;
|
||||
}
|
||||
public byte[] FrameBytes { get; private set; }
|
||||
public int Left { get; private set; }
|
||||
public int Top { get; private set; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public string ViewerID { get; private set; }
|
||||
public string MachineName { get; private set; }
|
||||
public DateTime StartTime { get; private set; }
|
||||
}
|
||||
}
|
||||
@ -59,6 +59,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.2" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.2" PrivateAssets="All" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -1699,4 +1700,11 @@
|
||||
<ProjectReference Include="..\Remotely_Library\Remotely_Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Drawing">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Drawing.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -17,10 +17,9 @@ namespace Remotely_Server.Services
|
||||
public string DefaultPrompt => Config["ApplicationOptions:DefaultPrompt"];
|
||||
public string DBProvider => Config["ApplicationOptions:DBProvider"];
|
||||
public bool AllowSelfRegistration => bool.Parse(Config["ApplicationOptions:AllowSelfRegistration"]);
|
||||
public bool UseDomainAuthentication => bool.Parse(Config["ApplicationOptions:UseDomainAuthentication"]);
|
||||
public bool ShowMessageOfTheDay => bool.Parse(Config["ApplicationOptions:ShowMessageOfTheDay"]);
|
||||
public double DataRetentionInDays => double.Parse(Config["ApplicationOptions:DataRetentionInDays"]);
|
||||
public double RemoteControlSessionLimit => double.Parse(Config["ApplicationOptions:RemoteControlSessionLimit"]);
|
||||
public bool RecordRemoteControlSessions => bool.Parse(Config["ApplicationOptions:RecordRemoteControlSessions"]);
|
||||
|
||||
public string SmtpHost => Config["ApplicationOptions:SmtpHost"];
|
||||
public int SmtpPort => int.Parse(Config["ApplicationOptions:SmtpPort"]);
|
||||
|
||||
@ -111,16 +111,6 @@ namespace Remotely_Server.Services
|
||||
}
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, RemotelyUser.Organization.ID);
|
||||
await Clients.Caller.SendAsync("UserOptions", RemotelyUser.UserOptions);
|
||||
if (AppConfig.ShowMessageOfTheDay)
|
||||
{
|
||||
try
|
||||
{
|
||||
var wc = new WebClient();
|
||||
var message = await wc.DownloadStringTaskAsync(new Uri("https://remotely.lucency.co/api/messageoftheday"));
|
||||
await Clients.Caller.SendAsync("DisplayConsoleHTML", message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
|
||||
@ -12,12 +12,17 @@ namespace Remotely_Server.Services
|
||||
{
|
||||
public class RCBrowserSocketHub : Hub
|
||||
{
|
||||
public RCBrowserSocketHub(DataService dataService, IHubContext<RCDeviceSocketHub> rcDeviceHub, IHubContext<DeviceSocketHub> deviceHub, ApplicationConfig appConfig)
|
||||
public RCBrowserSocketHub(DataService dataService,
|
||||
IHubContext<RCDeviceSocketHub> rcDeviceHub,
|
||||
IHubContext<DeviceSocketHub> deviceHub,
|
||||
ApplicationConfig appConfig,
|
||||
RemoteControlSessionRecorder rcSessionRecorder)
|
||||
{
|
||||
this.DataService = dataService;
|
||||
this.RCDeviceHub = rcDeviceHub;
|
||||
this.AppConfig = appConfig;
|
||||
this.DeviceHub = deviceHub;
|
||||
RCSessionRecorder = rcSessionRecorder;
|
||||
}
|
||||
public static ConcurrentDictionary<string, RemotelyUser> OrganizationConnectionList { get; set; } = new ConcurrentDictionary<string, RemotelyUser>();
|
||||
private ApplicationConfig AppConfig { get; set; }
|
||||
@ -47,6 +52,7 @@ namespace Remotely_Server.Services
|
||||
|
||||
private DataService DataService { get; }
|
||||
private IHubContext<DeviceSocketHub> DeviceHub { get; }
|
||||
private RemoteControlSessionRecorder RCSessionRecorder { get; }
|
||||
private IHubContext<RCDeviceSocketHub> RCDeviceHub { get; }
|
||||
private string RequesterName
|
||||
{
|
||||
@ -125,6 +131,11 @@ namespace Remotely_Server.Services
|
||||
}
|
||||
}
|
||||
await RCDeviceHub.Clients.Client(ScreenCasterID).SendAsync("ViewerDisconnected", Context.ConnectionId);
|
||||
|
||||
if (AppConfig.RecordRemoteControlSessions)
|
||||
{
|
||||
RCSessionRecorder.EncodeFrames(Context.ConnectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SelectScreen(int screenIndex)
|
||||
@ -150,6 +161,7 @@ namespace Remotely_Server.Services
|
||||
TimeStamp = DateTime.Now,
|
||||
Message = $"Remote control session requested by {requesterName}. " +
|
||||
$"Connection ID: {Context.ConnectionId}. User ID: {Context.UserIdentifier}. " +
|
||||
$"Screen Caster ID: {screenCasterID}." +
|
||||
$"Login ID (if logged in): {Context?.User?.Identity?.Name}. " +
|
||||
$"Requester IP Address: " + Context?.GetHttpContext()?.Connection?.RemoteIpAddress?.ToString()
|
||||
});
|
||||
|
||||
@ -1,29 +1,37 @@
|
||||
using Remotely_Library.Models;
|
||||
using Remotely_Library.Services;
|
||||
using Remotely_Server.Data;
|
||||
using Remotely_Server.Data;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.IO;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Remotely_Server.Services
|
||||
{
|
||||
public class RCDeviceSocketHub : Hub
|
||||
{
|
||||
public static ConcurrentDictionary<string, string> AttendedSessionList { get; set; } = new ConcurrentDictionary<string, string>();
|
||||
public static object SaveLock { get; } = new object();
|
||||
public RCDeviceSocketHub(DataService dataService,
|
||||
IHubContext<BrowserSocketHub> browserHub,
|
||||
IHubContext<RCBrowserSocketHub> rcBrowserHub,
|
||||
IHubContext<DeviceSocketHub> deviceSocketHub)
|
||||
IHubContext<DeviceSocketHub> deviceSocketHub,
|
||||
RemoteControlSessionRecorder rcSessionRecorder,
|
||||
ApplicationConfig appConfig)
|
||||
{
|
||||
DataService = dataService;
|
||||
BrowserHub = browserHub;
|
||||
RCBrowserHub = rcBrowserHub;
|
||||
DeviceHub = deviceSocketHub;
|
||||
RCSessionRecorder = rcSessionRecorder;
|
||||
AppConfig = appConfig;
|
||||
}
|
||||
private IHubContext<DeviceSocketHub> DeviceHub { get; }
|
||||
public RemoteControlSessionRecorder RCSessionRecorder { get; }
|
||||
public ApplicationConfig AppConfig { get; }
|
||||
private DataService DataService { get; }
|
||||
private IHubContext<BrowserSocketHub> BrowserHub { get; }
|
||||
private IHubContext<RCBrowserSocketHub> RCBrowserHub { get; }
|
||||
@ -45,6 +53,61 @@ namespace Remotely_Server.Services
|
||||
Context.Items["ServiceID"] = value;
|
||||
}
|
||||
}
|
||||
private string MachineName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.Items.ContainsKey("MachineName"))
|
||||
{
|
||||
return Context.Items["MachineName"] as string;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Context.Items["MachineName"] = value;
|
||||
}
|
||||
}
|
||||
private Size CurrentScreenSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.Items.ContainsKey("CurrentScreenSize"))
|
||||
{
|
||||
return (Size)Context.Items["CurrentScreenSize"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return Size.Empty;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Context.Items["CurrentScreenSize"] = value;
|
||||
}
|
||||
}
|
||||
private DateTime StartTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.Items.ContainsKey("StartTime"))
|
||||
{
|
||||
return (DateTime)Context.Items["StartTime"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Context.Items["StartTime"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> ViewerList
|
||||
{
|
||||
get
|
||||
@ -59,6 +122,7 @@ namespace Remotely_Server.Services
|
||||
|
||||
public override Task OnConnectedAsync()
|
||||
{
|
||||
StartTime = DateTime.Now;
|
||||
return base.OnConnectedAsync();
|
||||
}
|
||||
public override async Task OnDisconnectedAsync(Exception exception)
|
||||
@ -81,9 +145,10 @@ namespace Remotely_Server.Services
|
||||
}
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
public void ReceiveServiceID(string serviceID)
|
||||
public void ReceiveDeviceInfo(string serviceID, string machineName)
|
||||
{
|
||||
ServiceID = serviceID;
|
||||
MachineName = machineName;
|
||||
}
|
||||
public void ViewerDisconnected(string viewerID)
|
||||
{
|
||||
@ -103,14 +168,21 @@ namespace Remotely_Server.Services
|
||||
|
||||
public async Task SendScreenSize(int width, int height, string rcBrowserHubConnectionID)
|
||||
{
|
||||
CurrentScreenSize = new Size(width, height);
|
||||
await RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenSize", width, height);
|
||||
}
|
||||
|
||||
public Task SendScreenCapture(byte[] captureBytes, string rcBrowserHubConnectionID, int left, int top, int width, int height, DateTime captureTime)
|
||||
{
|
||||
if (AppConfig.RecordRemoteControlSessions)
|
||||
{
|
||||
RCSessionRecorder.SaveFrame(captureBytes, left, top, CurrentScreenSize.Width, CurrentScreenSize.Height, rcBrowserHubConnectionID, MachineName, StartTime);
|
||||
}
|
||||
|
||||
return RCBrowserHub.Clients.Client(rcBrowserHubConnectionID).SendAsync("ScreenCapture", captureBytes, left, top, width, height, captureTime);
|
||||
}
|
||||
|
||||
|
||||
public async Task NotifyRequesterUnattendedReady(string browserHubConnectionID)
|
||||
{
|
||||
await BrowserHub.Clients.Client(browserHubConnectionID).SendAsync("UnattendedSessionReady", Context.ConnectionId);
|
||||
|
||||
125
Remotely_Server/Services/RemoteControlSessionRecorder.cs
Normal file
125
Remotely_Server/Services/RemoteControlSessionRecorder.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Remotely_Server.Data;
|
||||
using Remotely_Server.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely_Server.Services
|
||||
{
|
||||
public class RemoteControlSessionRecorder
|
||||
{
|
||||
private static bool IsProcessing { get; set; }
|
||||
private static ConcurrentQueue<RemoteControlFrame> FrameQueue { get; } = new ConcurrentQueue<RemoteControlFrame>();
|
||||
private static ConcurrentDictionary<string, Bitmap> CumulativeFrames { get; } = new ConcurrentDictionary<string, Bitmap>();
|
||||
private static object LockObject { get; } = new object();
|
||||
|
||||
private IHostingEnvironment HostingEnv { get; }
|
||||
private DataService DataService { get; }
|
||||
|
||||
public RemoteControlSessionRecorder(IHostingEnvironment hostingEnv, DataService dataService)
|
||||
{
|
||||
HostingEnv = hostingEnv;
|
||||
DataService = dataService;
|
||||
}
|
||||
internal void SaveFrame(byte[] frameBytes, int left, int top, int width, int height, string viewerID, string machineName, DateTime startTime)
|
||||
{
|
||||
var rcFrame = new RemoteControlFrame(frameBytes, left, top, width, height, viewerID, machineName, startTime);
|
||||
FrameQueue.Enqueue(rcFrame);
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
if (!IsProcessing)
|
||||
{
|
||||
IsProcessing = true;
|
||||
Task.Run(StartProcessing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void StartProcessing()
|
||||
{
|
||||
while (FrameQueue.Count > 0)
|
||||
{
|
||||
if (FrameQueue.TryDequeue(out var frame))
|
||||
{
|
||||
if (!CumulativeFrames.ContainsKey(frame.ViewerID))
|
||||
{
|
||||
CumulativeFrames[frame.ViewerID] = new Bitmap(frame.Width, frame.Height);
|
||||
}
|
||||
|
||||
var saveDir = Directory.CreateDirectory(GetSaveFolder(frame));
|
||||
|
||||
var saveFile = Path.Combine(
|
||||
saveDir.FullName,
|
||||
$"frame-{(Directory.GetFiles(saveDir.FullName).Length + 1).ToString()}.jpg");
|
||||
|
||||
var bitmap = CumulativeFrames[frame.ViewerID] as Bitmap;
|
||||
using (var graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
using (var ms = new MemoryStream(frame.FrameBytes))
|
||||
{
|
||||
using (var saveImage = Image.FromStream(ms))
|
||||
{
|
||||
graphics.DrawImage(saveImage, frame.Left, frame.Top);
|
||||
}
|
||||
}
|
||||
}
|
||||
bitmap.Save(saveFile, ImageFormat.Jpeg);
|
||||
}
|
||||
}
|
||||
lock (LockObject)
|
||||
{
|
||||
IsProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSaveFolder(RemoteControlFrame frame)
|
||||
{
|
||||
return Path.Combine(
|
||||
HostingEnv.ContentRootPath,
|
||||
"Recordings",
|
||||
frame.StartTime.Year.ToString().PadLeft(4, '0'),
|
||||
frame.StartTime.Month.ToString().PadLeft(2, '0'),
|
||||
frame.StartTime.Day.ToString().PadLeft(2, '0'),
|
||||
frame.MachineName,
|
||||
frame.ViewerID,
|
||||
frame.StartTime.ToString("HH.mm.ss.fff"));
|
||||
}
|
||||
|
||||
internal void EncodeFrames(string viewerID)
|
||||
{
|
||||
var recordingDirs = Directory.GetDirectories(Path.Combine(
|
||||
HostingEnv.ContentRootPath,
|
||||
"Recordings",
|
||||
DateTime.Now.Year.ToString().PadLeft(4, '0'),
|
||||
DateTime.Now.Month.ToString().PadLeft(2, '0')),
|
||||
viewerID,
|
||||
SearchOption.AllDirectories);
|
||||
|
||||
foreach (var dir in recordingDirs)
|
||||
{
|
||||
foreach (var subDir in Directory.GetDirectories(dir))
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start("ffmpeg", $"-y -i \"{Path.Combine(subDir, "frame-%d.jpg")}\" \"{Path.Combine(subDir, "Recording.mp4")}\"").WaitForExit();
|
||||
foreach (var file in Directory.GetFiles(subDir, "*.jpg"))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DataService.WriteEvent(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,6 +94,7 @@ namespace Remotely_Server
|
||||
services.AddScoped<IEmailSender, EmailSender>();
|
||||
services.AddScoped<EmailSender>();
|
||||
services.AddScoped<DataService>();
|
||||
services.AddScoped<RemoteControlSessionRecorder>();
|
||||
services.AddSingleton<ApplicationConfig>();
|
||||
services.AddSingleton<RandomGenerator>();
|
||||
}
|
||||
|
||||
@ -13,8 +13,7 @@
|
||||
"DefaultPrompt": "~>",
|
||||
"DBProvider": "SQLite",
|
||||
"AllowSelfRegistration": true,
|
||||
"UseDomainAuthentication": false,
|
||||
"ShowMessageOfTheDay": true,
|
||||
"RecordRemoteControlSessions": false,
|
||||
"DataRetentionInDays": 90,
|
||||
"RemoteControlSessionLimit": 1,
|
||||
"SmtpHost": "",
|
||||
|
||||
@ -22,8 +22,6 @@ export class RCBrowserSockets {
|
||||
});
|
||||
this.Connection.closedCallbacks.push((ev) => {
|
||||
UI.Screen2DContext.clearRect(0, 0, UI.ScreenViewer.width, UI.ScreenViewer.height);
|
||||
console.log("Connection closed.");
|
||||
UI.StatusMessage.innerHTML = "Connection closed.";
|
||||
UI.ScreenViewer.setAttribute("hidden", "hidden");
|
||||
UI.ConnectBox.style.removeProperty("display");
|
||||
});
|
||||
@ -134,6 +132,7 @@ export class RCBrowserSockets {
|
||||
this.Connection.stop();
|
||||
});
|
||||
hubConnection.on("ScreenCasterDisconnected", () => {
|
||||
UI.StatusMessage.innerHTML = "The host has disconnected.";
|
||||
this.Connection.stop();
|
||||
});
|
||||
hubConnection.on("RelaunchedScreenCasterReady", (newClientID) => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -30,8 +30,6 @@ export class RCBrowserSockets {
|
||||
})
|
||||
this.Connection.closedCallbacks.push((ev) => {
|
||||
UI.Screen2DContext.clearRect(0, 0, UI.ScreenViewer.width, UI.ScreenViewer.height);
|
||||
console.log("Connection closed.");
|
||||
UI.StatusMessage.innerHTML = "Connection closed.";
|
||||
UI.ScreenViewer.setAttribute("hidden", "hidden");
|
||||
UI.ConnectBox.style.removeProperty("display");
|
||||
});
|
||||
@ -143,6 +141,7 @@ export class RCBrowserSockets {
|
||||
this.Connection.stop();
|
||||
});
|
||||
hubConnection.on("ScreenCasterDisconnected", () => {
|
||||
UI.StatusMessage.innerHTML = "The host has disconnected.";
|
||||
this.Connection.stop();
|
||||
});
|
||||
hubConnection.on("RelaunchedScreenCasterReady", (newClientID: string) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user