From b3ed309192684ae821a9361e11644d358a45f4db Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 9 Feb 2022 22:32:17 +0100 Subject: [PATCH] Code cleanup, 2022 edition --- Controllers/RtmpAuthController.cs | 2 +- DataModels/AppDb.cs | 4 +- DataModels/Tables/DbInfo.cs | 4 +- DataModels/Tables/Invite.cs | 10 +- Pages/Admin.cshtml.cs | 4 +- Pages/Content.cshtml.cs | 8 +- Pages/Credits.cshtml.cs | 8 +- Pages/Error.cshtml.cs | 16 +- Pages/Index.cshtml.cs | 8 +- Pages/Login.cshtml.cs | 112 +++++++------ Pages/Logout.cshtml.cs | 10 +- Pages/Privacy.cshtml.cs | 8 +- Pages/Stats.cshtml.cs | 8 +- Pages/profile.cshtml.cs | 12 +- Program.cs | 4 +- Properties/launchSettings.json | 2 +- RTMPDash.csproj | 64 +++++++- Startup.cs | 82 +++++----- StreamUtils.cs | 257 ++++++++++++++---------------- 19 files changed, 329 insertions(+), 294 deletions(-) diff --git a/Controllers/RtmpAuthController.cs b/Controllers/RtmpAuthController.cs index 55db290..9f57d12 100644 --- a/Controllers/RtmpAuthController.cs +++ b/Controllers/RtmpAuthController.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc; using RTMPDash.DataModels; -namespace RTMPDash.Controllers; +namespace RTMPDash.Controllers; [ApiController, Route("/api/authenticate")] public class RtmpAuthController : ControllerBase { diff --git a/DataModels/AppDb.cs b/DataModels/AppDb.cs index 2fe2ce2..80845ce 100644 --- a/DataModels/AppDb.cs +++ b/DataModels/AppDb.cs @@ -5,7 +5,7 @@ using LinqToDB.Configuration; using LinqToDB.Data; using RTMPDash.DataModels.Tables; -namespace RTMPDash.DataModels; +namespace RTMPDash.DataModels; public class AppDb { public class ConnectionStringSettings : IConnectionStringSettings { @@ -33,4 +33,4 @@ public class AppDb { public ITable Invites => GetTable(); public ITable DbInfo => GetTable(); } -} \ No newline at end of file +} diff --git a/DataModels/Tables/DbInfo.cs b/DataModels/Tables/DbInfo.cs index cc06981..084cb57 100644 --- a/DataModels/Tables/DbInfo.cs +++ b/DataModels/Tables/DbInfo.cs @@ -1,9 +1,9 @@ using LinqToDB.Mapping; -namespace RTMPDash.DataModels.Tables; +namespace RTMPDash.DataModels.Tables; [Table(Name = "DbInfo")] public class DbInfo { [Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; } [Column(Name = "DbVer"), NotNull] public int DbVer { get; set; } -} \ No newline at end of file +} diff --git a/DataModels/Tables/Invite.cs b/DataModels/Tables/Invite.cs index a2e3caa..125b264 100644 --- a/DataModels/Tables/Invite.cs +++ b/DataModels/Tables/Invite.cs @@ -1,8 +1,8 @@ using LinqToDB.Mapping; -namespace RTMPDash.DataModels.Tables { - [Table(Name = "Invites")] - public class Invite { - [Column(Name = "Code"), PrimaryKey, NotNull] public string Code { get; set; } - } +namespace RTMPDash.DataModels.Tables; + +[Table(Name = "Invites")] +public class Invite { + [Column(Name = "Code"), PrimaryKey, NotNull] public string Code { get; set; } } \ No newline at end of file diff --git a/Pages/Admin.cshtml.cs b/Pages/Admin.cshtml.cs index 5119f7a..f834694 100644 --- a/Pages/Admin.cshtml.cs +++ b/Pages/Admin.cshtml.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using RTMPDash.DataModels; using RTMPDash.DataModels.Tables; -namespace RTMPDash.Pages; +namespace RTMPDash.Pages; public class AdminModel : PageModel { public void OnGet() { } @@ -64,4 +64,4 @@ public class AdminModel : PageModel { Response.Redirect("/Admin"); } } -} \ No newline at end of file +} diff --git a/Pages/Content.cshtml.cs b/Pages/Content.cshtml.cs index f081860..cf53608 100644 --- a/Pages/Content.cshtml.cs +++ b/Pages/Content.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class ContentModel : PageModel { - public void OnGet() { } - } +namespace RTMPDash.Pages; + +public class ContentModel : PageModel { + public void OnGet() { } } \ No newline at end of file diff --git a/Pages/Credits.cshtml.cs b/Pages/Credits.cshtml.cs index 5a9b181..096cb1a 100644 --- a/Pages/Credits.cshtml.cs +++ b/Pages/Credits.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class CreditsModel : PageModel { - public void OnGet() { } - } +namespace RTMPDash.Pages; + +public class CreditsModel : PageModel { + public void OnGet() { } } \ No newline at end of file diff --git a/Pages/Error.cshtml.cs b/Pages/Error.cshtml.cs index bd93f64..10d69db 100644 --- a/Pages/Error.cshtml.cs +++ b/Pages/Error.cshtml.cs @@ -2,15 +2,15 @@ using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true), IgnoreAntiforgeryToken] - public class ErrorModel : PageModel { - public string RequestId { get; set; } +namespace RTMPDash.Pages; - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); +[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true), IgnoreAntiforgeryToken] +public class ErrorModel : PageModel { + public string RequestId { get; set; } - public void OnGet() { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + public void OnGet() { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; } } \ No newline at end of file diff --git a/Pages/Index.cshtml.cs b/Pages/Index.cshtml.cs index 8b6f723..ddb5b25 100644 --- a/Pages/Index.cshtml.cs +++ b/Pages/Index.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class IndexModel : PageModel { - public void OnGet() { } - } +namespace RTMPDash.Pages; + +public class IndexModel : PageModel { + public void OnGet() { } } \ No newline at end of file diff --git a/Pages/Login.cshtml.cs b/Pages/Login.cshtml.cs index 49346e8..137c08f 100644 --- a/Pages/Login.cshtml.cs +++ b/Pages/Login.cshtml.cs @@ -8,72 +8,68 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.RazorPages; using RTMPDash.DataModels; -namespace RTMPDash.Pages { - public class LoginModel : PageModel { - public void OnPost() { - if (!Request.HasFormContentType - || string.IsNullOrWhiteSpace(Request.Form["user"]) - || string.IsNullOrWhiteSpace(Request.Form["pass"])) - return; +namespace RTMPDash.Pages; - using var db = new AppDb.DbConn(); - var user = db.Users.FirstOrDefault(p => p.Username == Request.Form["user"].ToString() - && p.Password == Request.Form["pass"].ToString().Sha256()); - if (user == null) - return; +public class LoginModel : PageModel { + public void OnPost() { + if (!Request.HasFormContentType || string.IsNullOrWhiteSpace(Request.Form["user"]) || string.IsNullOrWhiteSpace(Request.Form["pass"])) + return; - HttpContext.Session.SetString("authenticatedUser", user.Username); - } + using var db = new AppDb.DbConn(); + var user = db.Users.FirstOrDefault(p => p.Username == Request.Form["user"].ToString() && p.Password == Request.Form["pass"].ToString().Sha256()); + if (user == null) + return; + + HttpContext.Session.SetString("authenticatedUser", user.Username); + } +} + +public static class StringExtensions { + public static string Sha256(this string rawData) { + // Create a SHA256 + using var sha256Hash = SHA256.Create(); + + // ComputeHash - returns byte array + var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData)); + + // Convert byte array to a string + var builder = new StringBuilder(); + for (var i = 0; i < bytes.Length; i++) + builder.Append(bytes[i].ToString("x2")); + + return builder.ToString(); } - public static class StringExtensions { - public static string Sha256(this string rawData) { - // Create a SHA256 - using var sha256Hash = SHA256.Create(); + public static string FirstCharToUpper(this string input) => input switch { + null => throw new ArgumentNullException(nameof(input)), + "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)), + _ => input.First().ToString().ToUpper() + input.Substring(1) + }; - // ComputeHash - returns byte array - var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData)); + public static string Base64Encode(this string plainText) { + var plainTextBytes = Encoding.UTF8.GetBytes(plainText); + return Convert.ToBase64String(plainTextBytes); + } - // Convert byte array to a string - var builder = new StringBuilder(); - for (var i = 0; i < bytes.Length; i++) - builder.Append(bytes[i].ToString("x2")); + public static string UrlEncode(this string plainText) => HttpUtility.UrlEncode(plainText); - return builder.ToString(); - } + public static string Delimit(this string input, int max) => input.PadRight(max, ' ').Substring(0, max).TrimEnd(); - public static string FirstCharToUpper(this string input) => input switch { - null => throw new ArgumentNullException(nameof(input)), - "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)), - _ => input.First().ToString().ToUpper() + input.Substring(1) + public static string Bash(this string cmd) { + var escapedArgs = cmd.Replace("\"", "\\\""); + + var process = new Process { + StartInfo = new ProcessStartInfo { + FileName = "/bin/bash", + Arguments = $"-c \"{escapedArgs}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } }; - - public static string Base64Encode(this string plainText) { - var plainTextBytes = Encoding.UTF8.GetBytes(plainText); - return Convert.ToBase64String(plainTextBytes); - } - - public static string UrlEncode(this string plainText) => HttpUtility.UrlEncode(plainText); - - public static string Delimit(this string input, int max) => - input.PadRight(max, ' ').Substring(0, max).TrimEnd(); - - public static string Bash(this string cmd) { - var escapedArgs = cmd.Replace("\"", "\\\""); - - var process = new Process { - StartInfo = new ProcessStartInfo { - FileName = "/bin/bash", - Arguments = $"-c \"{escapedArgs}\"", - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - } - }; - process.Start(); - var result = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - return result; - } + process.Start(); + var result = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + return result; } } \ No newline at end of file diff --git a/Pages/Logout.cshtml.cs b/Pages/Logout.cshtml.cs index 86d5952..227efa7 100644 --- a/Pages/Logout.cshtml.cs +++ b/Pages/Logout.cshtml.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class LogoutModel : PageModel { - public void OnGet() { - HttpContext.Session.Clear(); - } +namespace RTMPDash.Pages; + +public class LogoutModel : PageModel { + public void OnGet() { + HttpContext.Session.Clear(); } } \ No newline at end of file diff --git a/Pages/Privacy.cshtml.cs b/Pages/Privacy.cshtml.cs index 86b21ac..ac838a5 100644 --- a/Pages/Privacy.cshtml.cs +++ b/Pages/Privacy.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class PrivacyModel : PageModel { - public void OnGet() { } - } +namespace RTMPDash.Pages; + +public class PrivacyModel : PageModel { + public void OnGet() { } } \ No newline at end of file diff --git a/Pages/Stats.cshtml.cs b/Pages/Stats.cshtml.cs index b1cda7c..10b0638 100644 --- a/Pages/Stats.cshtml.cs +++ b/Pages/Stats.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class StatsModel : PageModel { - public void OnGet() { } - } +namespace RTMPDash.Pages; + +public class StatsModel : PageModel { + public void OnGet() { } } \ No newline at end of file diff --git a/Pages/profile.cshtml.cs b/Pages/profile.cshtml.cs index 5dd37bb..d6d04d2 100644 --- a/Pages/profile.cshtml.cs +++ b/Pages/profile.cshtml.cs @@ -1,11 +1,11 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace RTMPDash.Pages { - public class ProfileModel : PageModel { - public new string User { get; set; } +namespace RTMPDash.Pages; - public void OnGet(string user) { - User = user; - } +public class ProfileModel : PageModel { + public new string User { get; set; } + + public void OnGet(string user) { + User = user; } } \ No newline at end of file diff --git a/Program.cs b/Program.cs index 0b5ed00..3eb78db 100644 --- a/Program.cs +++ b/Program.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using RTMPDash.DataModels; -namespace RTMPDash; +namespace RTMPDash; public class Program { public const string SiteName = "chaos.stream"; @@ -35,4 +35,4 @@ public class Program { public static class TimeExtensions { public static TimeSpan StripMilliseconds(this TimeSpan time) => new(time.Days, time.Hours, time.Minutes, time.Seconds); -} \ No newline at end of file +} diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 1b8a7e6..9d08fb9 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -1,7 +1,7 @@ { "iisSettings": { "windowsAuthentication": false, - "anonymousAuthentication": true, + "anonymousAuthentication": true }, "profiles": { "RTMPDash": { diff --git a/RTMPDash.csproj b/RTMPDash.csproj index 22bf0dd..47347a9 100644 --- a/RTMPDash.csproj +++ b/RTMPDash.csproj @@ -6,14 +6,68 @@ - - - - + + + + - <_ContentIncludedByDefault Remove="Areas\Identity\Pages\_ViewStart.cshtml" /> + <_ContentIncludedByDefault Remove="Areas\Identity\Pages\_ViewStart.cshtml"/> + <_ContentIncludedByDefault Remove="wwwroot\css\dark-mode.css"/> + <_ContentIncludedByDefault Remove="wwwroot\css\site.css"/> + <_ContentIncludedByDefault Remove="wwwroot\favicon.ico"/> + <_ContentIncludedByDefault Remove="wwwroot\js\dark-mode-switch.js"/> + <_ContentIncludedByDefault Remove="wwwroot\js\site.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\LICENSE"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation-unobtrusive\LICENSE.txt"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\additional-methods.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\additional-methods.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\jquery.validate.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\dist\jquery.validate.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery-validation\LICENSE.md"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery\dist\jquery.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery\dist\jquery.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery\dist\jquery.min.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\jquery\LICENSE.txt"/> + + + + + + + + + + + + + + + + diff --git a/Startup.cs b/Startup.cs index f84ab07..25222d4 100644 --- a/Startup.cs +++ b/Startup.cs @@ -5,61 +5,61 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace RTMPDash { - public class Startup { - public Startup(IConfiguration configuration) => Configuration = configuration; +namespace RTMPDash; - public IConfiguration Configuration { get; } +public class Startup { + public Startup(IConfiguration configuration) => Configuration = configuration; - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) { - services.AddRazorPages(); - services.AddSession(options => { - options.IdleTimeout = TimeSpan.MaxValue; - options.Cookie.HttpOnly = true; - options.Cookie.IsEssential = true; - }); + public IConfiguration Configuration { get; } - #if (DEBUG) - services.AddControllers().AddRazorRuntimeCompilation(); - services.AddStackExchangeRedisCache(options => { - options.Configuration = "localhost"; - options.InstanceName = "RTMPdash_development"; - }); - #else + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) { + services.AddRazorPages(); + services.AddSession(options => { + options.IdleTimeout = TimeSpan.MaxValue; + options.Cookie.HttpOnly = true; + options.Cookie.IsEssential = true; + }); + + #if (DEBUG) + services.AddControllers().AddRazorRuntimeCompilation(); + services.AddStackExchangeRedisCache(options => { + options.Configuration = "localhost"; + options.InstanceName = "RTMPdash_development"; + }); + #else services.AddControllers(); services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "RTMPdash_production"; }); - #endif + #endif + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + if (env.IsDevelopment()) { + app.UseDeveloperExceptionPage(); + } + else { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) { - app.UseDeveloperExceptionPage(); - } - else { - app.UseExceptionHandler("/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } + app.UseStaticFiles(); - app.UseStaticFiles(); + app.UseSession(); - app.UseSession(); + app.UseRouting(); - app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => { - endpoints.MapRazorPages(); - endpoints.MapControllers(); - }); - } + app.UseEndpoints(endpoints => { + endpoints.MapRazorPages(); + endpoints.MapControllers(); + }); } } \ No newline at end of file diff --git a/StreamUtils.cs b/StreamUtils.cs index eca9945..bb88968 100644 --- a/StreamUtils.cs +++ b/StreamUtils.cs @@ -4,151 +4,136 @@ using System.Net; using System.Xml.Serialization; using RTMPDash.DataModels; -namespace RTMPDash { - public static class StreamUtils { - private static readonly XmlSerializer Serializer = new(typeof(StatsObject)); +namespace RTMPDash; - public static bool IsLive(string user, StatsObject stats) => stats.Server.Applications - .First(p => p.Name == "ingress") - .MethodLive.Streams.Any(p => p.Name == user); +public static class StreamUtils { + private static readonly XmlSerializer Serializer = new(typeof(StatsObject)); - public static bool IsLive(string user, string target, StatsObject stats) => stats.Server.Applications - .First(p => p.Name == "ingress") - .MethodLive.Streams - .Any(p => p.Name == user && p.Clients.Any(c => c.Address == target.Replace("rtmp://", ""))); + public static bool IsLive(string user, StatsObject stats) => stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.Any(p => p.Name == user); - public static long GetClientTime(string user, StatsObject stats) => - long.Parse(stats.Server.Applications.First(p => p.Name == "ingress") - .MethodLive.Streams.First(p => p.Name == user) - .Time); + public static bool IsLive(string user, string target, StatsObject stats) => stats.Server.Applications.First(p => p.Name == "ingress") + .MethodLive.Streams + .Any(p => p.Name == user && p.Clients.Any(c => c.Address == target.Replace("rtmp://", ""))); - public static int CountLiveRestreams(string user, StatsObject stats) { - var db = new AppDb.DbConn(); - var dbUser = db.Users.First(p => p.Username == user); - return dbUser.RestreamTargets.Split(",") - .Count(target => stats.Server.Applications.First(p => p.Name == "ingress") - .MethodLive.Streams - .Any(p => p.Name == user - && p.Clients.Any(c => c.Address - == target.Replace("rtmp://", "")))); - } + public static long GetClientTime(string user, StatsObject stats) => + long.Parse(stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.First(p => p.Name == user).Time); - public static int CountLiveRestreams(string user, string privateAccesskey, StatsObject stats) { - var db = new AppDb.DbConn(); - var dbUser = db.Users.First(p => p.Username == user); - return dbUser.RestreamTargets.Split(",") - .Count(target => stats.Server.Applications.First(p => p.Name == "ingress") - .MethodLive.Streams - .Any(p => p.Name == privateAccesskey - && p.Clients.Any(c => c.Address - == target.Replace("rtmp://", "")))); - } - - - public static List ListLiveUsers() => GetStatsObject() - .Server.Applications.First(p => p.Name == "ingress") - .MethodLive.Streams.Select(p => p.Name) - .ToList(); - - public static StatsObject GetStatsObject() { - var obj = (StatsObject) Serializer.Deserialize(new WebClient().OpenRead("http://127.0.0.1:8080")!); - return obj; - } + public static int CountLiveRestreams(string user, StatsObject stats) { + var db = new AppDb.DbConn(); + var dbUser = db.Users.First(p => p.Username == user); + return dbUser.RestreamTargets.Split(",") + .Count(target => stats.Server.Applications.First(p => p.Name == "ingress") + .MethodLive.Streams.Any(p => p.Name == user && p.Clients.Any(c => c.Address == target.Replace("rtmp://", "")))); } - [XmlRoot(ElementName = "rtmp")] - public class StatsObject { - [XmlElement(ElementName = "nginx_version", IsNullable = true)] - public string NginxVersion { get; set; } - - [XmlElement(ElementName = "nginx_rtmp_version", IsNullable = true)] - public string NginxRtmpVersion { get; set; } - - [XmlElement(ElementName = "server", IsNullable = true)] public Server Server { get; set; } - [XmlElement(ElementName = "built", IsNullable = true)] public string Built { get; set; } - [XmlElement(ElementName = "pid", IsNullable = true)] public string Pid { get; set; } - [XmlElement(ElementName = "uptime", IsNullable = true)] public string Uptime { get; set; } - [XmlElement(ElementName = "naccepted", IsNullable = true)] public string AcceptedConnections { get; set; } - [XmlElement(ElementName = "bw_in", IsNullable = true)] public string BwIn { get; set; } - [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } - [XmlElement(ElementName = "bw_out", IsNullable = true)] public string BwOut { get; set; } - [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } + public static int CountLiveRestreams(string user, string privateAccesskey, StatsObject stats) { + var db = new AppDb.DbConn(); + var dbUser = db.Users.First(p => p.Username == user); + return dbUser.RestreamTargets.Split(",") + .Count(target => stats.Server.Applications.First(p => p.Name == "ingress") + .MethodLive.Streams.Any(p => p.Name == privateAccesskey && p.Clients.Any(c => c.Address == target.Replace("rtmp://", "")))); } - [XmlRoot(ElementName = "server", IsNullable = true)] - public class Server { - [XmlElement(ElementName = "application", IsNullable = true)] public List Applications { get; set; } - } + public static List ListLiveUsers() => GetStatsObject().Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.Select(p => p.Name).ToList(); - [XmlRoot(ElementName = "application", IsNullable = true)] - public class Application { - [XmlElement(ElementName = "live", IsNullable = true)] public MethodLive MethodLive { get; set; } - [XmlElement(ElementName = "name", IsNullable = true)] public string Name { get; set; } - } - - [XmlRoot(ElementName = "live", IsNullable = true)] - public class MethodLive { - [XmlElement(ElementName = "stream", IsNullable = true)] public List Streams { get; set; } - [XmlElement(ElementName = "nclients", IsNullable = true)] public string NoClients { get; set; } - } - - [XmlRoot(ElementName = "stream", IsNullable = true)] - public class Stream { - [XmlElement(ElementName = "client", IsNullable = true)] public List Clients { get; set; } - [XmlElement(ElementName = "meta", IsNullable = true)] public Meta Meta { get; set; } - [XmlElement(ElementName = "name", IsNullable = true)] public string Name { get; set; } - [XmlElement(ElementName = "time", IsNullable = true)] public string Time { get; set; } - [XmlElement(ElementName = "bw_in", IsNullable = true)] public string BwIn { get; set; } - [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } - [XmlElement(ElementName = "bw_out", IsNullable = true)] public string BwOut { get; set; } - [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } - [XmlElement(ElementName = "bw_audio", IsNullable = true)] public string BwAudio { get; set; } - [XmlElement(ElementName = "bw_video", IsNullable = true)] public string BwVideo { get; set; } - [XmlElement(ElementName = "bw_data", IsNullable = true)] public string BwData { get; set; } - [XmlElement(ElementName = "nclients", IsNullable = true)] public string NoClients { get; set; } - [XmlElement(ElementName = "active", IsNullable = true)] public object Active { get; set; } - [XmlElement(ElementName = "publishing", IsNullable = true)] public object Publishing { get; set; } - } - - [XmlRoot(ElementName = "client", IsNullable = true)] - public class Client { - [XmlElement(ElementName = "id", IsNullable = true)] public string Id { get; set; } - [XmlElement(ElementName = "address", IsNullable = true)] public string Address { get; set; } - [XmlElement(ElementName = "port", IsNullable = true)] public string Port { get; set; } - [XmlElement(ElementName = "time", IsNullable = true)] public string Time { get; set; } - [XmlElement(ElementName = "flashver", IsNullable = true)] public string FlashVer { get; set; } - [XmlElement(ElementName = "swfurl", IsNullable = true)] public string SwfUrl { get; set; } - [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } - [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } - [XmlElement(ElementName = "dropped", IsNullable = true)] public string Dropped { get; set; } - [XmlElement(ElementName = "avsync", IsNullable = true)] public string AvSync { get; set; } - [XmlElement(ElementName = "timestamp", IsNullable = true)] public string Timestamp { get; set; } - [XmlElement(ElementName = "active", IsNullable = true)] public object Active { get; set; } - [XmlElement(ElementName = "publishing", IsNullable = true)] public object Publishing { get; set; } - } - - [XmlRoot(ElementName = "meta", IsNullable = true)] - public class Meta { - [XmlElement(ElementName = "video", IsNullable = true)] public Video Video { get; set; } - [XmlElement(ElementName = "audio", IsNullable = true)] public Audio Audio { get; set; } - } - - [XmlRoot(ElementName = "video", IsNullable = true)] - public class Video { - [XmlElement(ElementName = "width", IsNullable = true)] public string Width { get; set; } - [XmlElement(ElementName = "height", IsNullable = true)] public string Height { get; set; } - [XmlElement(ElementName = "frame_rate", IsNullable = true)] public string FrameRate { get; set; } - [XmlElement(ElementName = "codec", IsNullable = true)] public string Codec { get; set; } - [XmlElement(ElementName = "profile", IsNullable = true)] public string Profile { get; set; } - [XmlElement(ElementName = "compat", IsNullable = true)] public string Compat { get; set; } - [XmlElement(ElementName = "level", IsNullable = true)] public string Level { get; set; } - } - - [XmlRoot(ElementName = "audio", IsNullable = true)] - public class Audio { - [XmlElement(ElementName = "codec", IsNullable = true)] public string Codec { get; set; } - [XmlElement(ElementName = "profile", IsNullable = true)] public string Profile { get; set; } - [XmlElement(ElementName = "channels", IsNullable = true)] public string Channels { get; set; } - [XmlElement(ElementName = "sample_rate", IsNullable = true)] public string SampleRate { get; set; } + public static StatsObject GetStatsObject() { + var obj = (StatsObject)Serializer.Deserialize(new WebClient().OpenRead("http://127.0.0.1:8080")!); + return obj; } } + +[XmlRoot(ElementName = "rtmp")] +public class StatsObject { + [XmlElement(ElementName = "nginx_version", IsNullable = true)] + public string NginxVersion { get; set; } + + [XmlElement(ElementName = "nginx_rtmp_version", IsNullable = true)] + public string NginxRtmpVersion { get; set; } + + [XmlElement(ElementName = "server", IsNullable = true)] public Server Server { get; set; } + [XmlElement(ElementName = "built", IsNullable = true)] public string Built { get; set; } + [XmlElement(ElementName = "pid", IsNullable = true)] public string Pid { get; set; } + [XmlElement(ElementName = "uptime", IsNullable = true)] public string Uptime { get; set; } + [XmlElement(ElementName = "naccepted", IsNullable = true)] public string AcceptedConnections { get; set; } + [XmlElement(ElementName = "bw_in", IsNullable = true)] public string BwIn { get; set; } + [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } + [XmlElement(ElementName = "bw_out", IsNullable = true)] public string BwOut { get; set; } + [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } +} + +[XmlRoot(ElementName = "server", IsNullable = true)] +public class Server { + [XmlElement(ElementName = "application", IsNullable = true)] public List Applications { get; set; } +} + +[XmlRoot(ElementName = "application", IsNullable = true)] +public class Application { + [XmlElement(ElementName = "live", IsNullable = true)] public MethodLive MethodLive { get; set; } + [XmlElement(ElementName = "name", IsNullable = true)] public string Name { get; set; } +} + +[XmlRoot(ElementName = "live", IsNullable = true)] +public class MethodLive { + [XmlElement(ElementName = "stream", IsNullable = true)] public List Streams { get; set; } + [XmlElement(ElementName = "nclients", IsNullable = true)] public string NoClients { get; set; } +} + +[XmlRoot(ElementName = "stream", IsNullable = true)] +public class Stream { + [XmlElement(ElementName = "client", IsNullable = true)] public List Clients { get; set; } + [XmlElement(ElementName = "meta", IsNullable = true)] public Meta Meta { get; set; } + [XmlElement(ElementName = "name", IsNullable = true)] public string Name { get; set; } + [XmlElement(ElementName = "time", IsNullable = true)] public string Time { get; set; } + [XmlElement(ElementName = "bw_in", IsNullable = true)] public string BwIn { get; set; } + [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } + [XmlElement(ElementName = "bw_out", IsNullable = true)] public string BwOut { get; set; } + [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } + [XmlElement(ElementName = "bw_audio", IsNullable = true)] public string BwAudio { get; set; } + [XmlElement(ElementName = "bw_video", IsNullable = true)] public string BwVideo { get; set; } + [XmlElement(ElementName = "bw_data", IsNullable = true)] public string BwData { get; set; } + [XmlElement(ElementName = "nclients", IsNullable = true)] public string NoClients { get; set; } + [XmlElement(ElementName = "active", IsNullable = true)] public object Active { get; set; } + [XmlElement(ElementName = "publishing", IsNullable = true)] public object Publishing { get; set; } +} + +[XmlRoot(ElementName = "client", IsNullable = true)] +public class Client { + [XmlElement(ElementName = "id", IsNullable = true)] public string Id { get; set; } + [XmlElement(ElementName = "address", IsNullable = true)] public string Address { get; set; } + [XmlElement(ElementName = "port", IsNullable = true)] public string Port { get; set; } + [XmlElement(ElementName = "time", IsNullable = true)] public string Time { get; set; } + [XmlElement(ElementName = "flashver", IsNullable = true)] public string FlashVer { get; set; } + [XmlElement(ElementName = "swfurl", IsNullable = true)] public string SwfUrl { get; set; } + [XmlElement(ElementName = "bytes_in", IsNullable = true)] public string BytesIn { get; set; } + [XmlElement(ElementName = "bytes_out", IsNullable = true)] public string BytesOut { get; set; } + [XmlElement(ElementName = "dropped", IsNullable = true)] public string Dropped { get; set; } + [XmlElement(ElementName = "avsync", IsNullable = true)] public string AvSync { get; set; } + [XmlElement(ElementName = "timestamp", IsNullable = true)] public string Timestamp { get; set; } + [XmlElement(ElementName = "active", IsNullable = true)] public object Active { get; set; } + [XmlElement(ElementName = "publishing", IsNullable = true)] public object Publishing { get; set; } +} + +[XmlRoot(ElementName = "meta", IsNullable = true)] +public class Meta { + [XmlElement(ElementName = "video", IsNullable = true)] public Video Video { get; set; } + [XmlElement(ElementName = "audio", IsNullable = true)] public Audio Audio { get; set; } +} + +[XmlRoot(ElementName = "video", IsNullable = true)] +public class Video { + [XmlElement(ElementName = "width", IsNullable = true)] public string Width { get; set; } + [XmlElement(ElementName = "height", IsNullable = true)] public string Height { get; set; } + [XmlElement(ElementName = "frame_rate", IsNullable = true)] public string FrameRate { get; set; } + [XmlElement(ElementName = "codec", IsNullable = true)] public string Codec { get; set; } + [XmlElement(ElementName = "profile", IsNullable = true)] public string Profile { get; set; } + [XmlElement(ElementName = "compat", IsNullable = true)] public string Compat { get; set; } + [XmlElement(ElementName = "level", IsNullable = true)] public string Level { get; set; } +} + +[XmlRoot(ElementName = "audio", IsNullable = true)] +public class Audio { + [XmlElement(ElementName = "codec", IsNullable = true)] public string Codec { get; set; } + [XmlElement(ElementName = "profile", IsNullable = true)] public string Profile { get; set; } + [XmlElement(ElementName = "channels", IsNullable = true)] public string Channels { get; set; } + [XmlElement(ElementName = "sample_rate", IsNullable = true)] public string SampleRate { get; set; } +}