Code cleanup, 2022 edition
This commit is contained in:
parent
a5f66216c9
commit
b3ed309192
|
@ -2,7 +2,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash.Controllers;
|
namespace RTMPDash.Controllers;
|
||||||
|
|
||||||
[ApiController, Route("/api/authenticate")]
|
[ApiController, Route("/api/authenticate")]
|
||||||
public class RtmpAuthController : ControllerBase {
|
public class RtmpAuthController : ControllerBase {
|
||||||
|
|
|
@ -5,7 +5,7 @@ using LinqToDB.Configuration;
|
||||||
using LinqToDB.Data;
|
using LinqToDB.Data;
|
||||||
using RTMPDash.DataModels.Tables;
|
using RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
namespace RTMPDash.DataModels;
|
namespace RTMPDash.DataModels;
|
||||||
|
|
||||||
public class AppDb {
|
public class AppDb {
|
||||||
public class ConnectionStringSettings : IConnectionStringSettings {
|
public class ConnectionStringSettings : IConnectionStringSettings {
|
||||||
|
@ -33,4 +33,4 @@ public class AppDb {
|
||||||
public ITable<Invite> Invites => GetTable<Invite>();
|
public ITable<Invite> Invites => GetTable<Invite>();
|
||||||
public ITable<DbInfo> DbInfo => GetTable<DbInfo>();
|
public ITable<DbInfo> DbInfo => GetTable<DbInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using LinqToDB.Mapping;
|
using LinqToDB.Mapping;
|
||||||
|
|
||||||
namespace RTMPDash.DataModels.Tables;
|
namespace RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
[Table(Name = "DbInfo")]
|
[Table(Name = "DbInfo")]
|
||||||
public class DbInfo {
|
public class DbInfo {
|
||||||
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
||||||
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
|
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using LinqToDB.Mapping;
|
using LinqToDB.Mapping;
|
||||||
|
|
||||||
namespace RTMPDash.DataModels.Tables {
|
namespace RTMPDash.DataModels.Tables;
|
||||||
[Table(Name = "Invites")]
|
|
||||||
public class Invite {
|
[Table(Name = "Invites")]
|
||||||
[Column(Name = "Code"), PrimaryKey, NotNull] public string Code { get; set; }
|
public class Invite {
|
||||||
}
|
[Column(Name = "Code"), PrimaryKey, NotNull] public string Code { get; set; }
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
using RTMPDash.DataModels.Tables;
|
using RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
namespace RTMPDash.Pages;
|
namespace RTMPDash.Pages;
|
||||||
|
|
||||||
public class AdminModel : PageModel {
|
public class AdminModel : PageModel {
|
||||||
public void OnGet() { }
|
public void OnGet() { }
|
||||||
|
@ -64,4 +64,4 @@ public class AdminModel : PageModel {
|
||||||
Response.Redirect("/Admin");
|
Response.Redirect("/Admin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class ContentModel : PageModel {
|
|
||||||
public void OnGet() { }
|
public class ContentModel : PageModel {
|
||||||
}
|
public void OnGet() { }
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class CreditsModel : PageModel {
|
|
||||||
public void OnGet() { }
|
public class CreditsModel : PageModel {
|
||||||
}
|
public void OnGet() { }
|
||||||
}
|
}
|
|
@ -2,15 +2,15 @@ using System.Diagnostics;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true), IgnoreAntiforgeryToken]
|
|
||||||
public class ErrorModel : PageModel {
|
|
||||||
public string RequestId { get; set; }
|
|
||||||
|
|
||||||
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() {
|
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
|
||||||
}
|
public void OnGet() {
|
||||||
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class IndexModel : PageModel {
|
|
||||||
public void OnGet() { }
|
public class IndexModel : PageModel {
|
||||||
}
|
public void OnGet() { }
|
||||||
}
|
}
|
|
@ -8,72 +8,68 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class LoginModel : PageModel {
|
|
||||||
public void OnPost() {
|
|
||||||
if (!Request.HasFormContentType
|
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["user"])
|
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["pass"]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var db = new AppDb.DbConn();
|
public class LoginModel : PageModel {
|
||||||
var user = db.Users.FirstOrDefault(p => p.Username == Request.Form["user"].ToString()
|
public void OnPost() {
|
||||||
&& p.Password == Request.Form["pass"].ToString().Sha256());
|
if (!Request.HasFormContentType || string.IsNullOrWhiteSpace(Request.Form["user"]) || string.IsNullOrWhiteSpace(Request.Form["pass"]))
|
||||||
if (user == null)
|
return;
|
||||||
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 FirstCharToUpper(this string input) => input switch {
|
||||||
public static string Sha256(this string rawData) {
|
null => throw new ArgumentNullException(nameof(input)),
|
||||||
// Create a SHA256
|
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
|
||||||
using var sha256Hash = SHA256.Create();
|
_ => input.First().ToString().ToUpper() + input.Substring(1)
|
||||||
|
};
|
||||||
|
|
||||||
// ComputeHash - returns byte array
|
public static string Base64Encode(this string plainText) {
|
||||||
var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
|
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||||
|
return Convert.ToBase64String(plainTextBytes);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert byte array to a string
|
public static string UrlEncode(this string plainText) => HttpUtility.UrlEncode(plainText);
|
||||||
var builder = new StringBuilder();
|
|
||||||
for (var i = 0; i < bytes.Length; i++)
|
|
||||||
builder.Append(bytes[i].ToString("x2"));
|
|
||||||
|
|
||||||
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 {
|
public static string Bash(this string cmd) {
|
||||||
null => throw new ArgumentNullException(nameof(input)),
|
var escapedArgs = cmd.Replace("\"", "\\\"");
|
||||||
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
|
|
||||||
_ => input.First().ToString().ToUpper() + input.Substring(1)
|
var process = new Process {
|
||||||
|
StartInfo = new ProcessStartInfo {
|
||||||
|
FileName = "/bin/bash",
|
||||||
|
Arguments = $"-c \"{escapedArgs}\"",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
process.Start();
|
||||||
public static string Base64Encode(this string plainText) {
|
var result = process.StandardOutput.ReadToEnd();
|
||||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
process.WaitForExit();
|
||||||
return Convert.ToBase64String(plainTextBytes);
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class LogoutModel : PageModel {
|
|
||||||
public void OnGet() {
|
public class LogoutModel : PageModel {
|
||||||
HttpContext.Session.Clear();
|
public void OnGet() {
|
||||||
}
|
HttpContext.Session.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class PrivacyModel : PageModel {
|
|
||||||
public void OnGet() { }
|
public class PrivacyModel : PageModel {
|
||||||
}
|
public void OnGet() { }
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class StatsModel : PageModel {
|
|
||||||
public void OnGet() { }
|
public class StatsModel : PageModel {
|
||||||
}
|
public void OnGet() { }
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class ProfileModel : PageModel {
|
|
||||||
public new string User { get; set; }
|
|
||||||
|
|
||||||
public void OnGet(string user) {
|
public class ProfileModel : PageModel {
|
||||||
User = user;
|
public new string User { get; set; }
|
||||||
}
|
|
||||||
|
public void OnGet(string user) {
|
||||||
|
User = user;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash;
|
namespace RTMPDash;
|
||||||
|
|
||||||
public class Program {
|
public class Program {
|
||||||
public const string SiteName = "chaos.stream";
|
public const string SiteName = "chaos.stream";
|
||||||
|
@ -35,4 +35,4 @@ public class Program {
|
||||||
|
|
||||||
public static class TimeExtensions {
|
public static class TimeExtensions {
|
||||||
public static TimeSpan StripMilliseconds(this TimeSpan time) => new(time.Days, time.Hours, time.Minutes, time.Seconds);
|
public static TimeSpan StripMilliseconds(this TimeSpan time) => new(time.Days, time.Hours, time.Minutes, time.Seconds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"iisSettings": {
|
"iisSettings": {
|
||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
"anonymousAuthentication": true,
|
"anonymousAuthentication": true
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"RTMPDash": {
|
"RTMPDash": {
|
||||||
|
|
|
@ -6,14 +6,68 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="linq2db" Version="3.2.3" />
|
<PackageReference Include="linq2db" Version="3.2.3"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.2"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1"/>
|
||||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.113.7" />
|
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.113.7"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_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"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="wwwroot\**"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Remove="wwwroot\**"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="wwwroot\**"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="wwwroot\**"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
82
Startup.cs
82
Startup.cs
|
@ -5,61 +5,61 @@ using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace RTMPDash {
|
namespace RTMPDash;
|
||||||
public class Startup {
|
|
||||||
public Startup(IConfiguration configuration) => Configuration = configuration;
|
|
||||||
|
|
||||||
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 IConfiguration Configuration { get; }
|
||||||
public void ConfigureServices(IServiceCollection services) {
|
|
||||||
services.AddRazorPages();
|
|
||||||
services.AddSession(options => {
|
|
||||||
options.IdleTimeout = TimeSpan.MaxValue;
|
|
||||||
options.Cookie.HttpOnly = true;
|
|
||||||
options.Cookie.IsEssential = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
#if (DEBUG)
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
services.AddControllers().AddRazorRuntimeCompilation();
|
public void ConfigureServices(IServiceCollection services) {
|
||||||
services.AddStackExchangeRedisCache(options => {
|
services.AddRazorPages();
|
||||||
options.Configuration = "localhost";
|
services.AddSession(options => {
|
||||||
options.InstanceName = "RTMPdash_development";
|
options.IdleTimeout = TimeSpan.MaxValue;
|
||||||
});
|
options.Cookie.HttpOnly = true;
|
||||||
#else
|
options.Cookie.IsEssential = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
#if (DEBUG)
|
||||||
|
services.AddControllers().AddRazorRuntimeCompilation();
|
||||||
|
services.AddStackExchangeRedisCache(options => {
|
||||||
|
options.Configuration = "localhost";
|
||||||
|
options.InstanceName = "RTMPdash_development";
|
||||||
|
});
|
||||||
|
#else
|
||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
services.AddStackExchangeRedisCache(options =>
|
services.AddStackExchangeRedisCache(options =>
|
||||||
{
|
{
|
||||||
options.Configuration = "localhost";
|
options.Configuration = "localhost";
|
||||||
options.InstanceName = "RTMPdash_production";
|
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.
|
app.UseStaticFiles();
|
||||||
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.UseSession();
|
||||||
|
|
||||||
app.UseSession();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseEndpoints(endpoints => {
|
||||||
app.UseAuthorization();
|
endpoints.MapRazorPages();
|
||||||
|
endpoints.MapControllers();
|
||||||
app.UseEndpoints(endpoints => {
|
});
|
||||||
endpoints.MapRazorPages();
|
|
||||||
endpoints.MapControllers();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
257
StreamUtils.cs
257
StreamUtils.cs
|
@ -4,151 +4,136 @@ using System.Net;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash {
|
namespace RTMPDash;
|
||||||
public static class StreamUtils {
|
|
||||||
private static readonly XmlSerializer Serializer = new(typeof(StatsObject));
|
|
||||||
|
|
||||||
public static bool IsLive(string user, StatsObject stats) => stats.Server.Applications
|
public static class StreamUtils {
|
||||||
.First(p => p.Name == "ingress")
|
private static readonly XmlSerializer Serializer = new(typeof(StatsObject));
|
||||||
.MethodLive.Streams.Any(p => p.Name == user);
|
|
||||||
|
|
||||||
public static bool IsLive(string user, string target, StatsObject stats) => stats.Server.Applications
|
public static bool IsLive(string user, StatsObject stats) => stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.Any(p => p.Name == user);
|
||||||
.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) =>
|
public static bool IsLive(string user, string target, StatsObject stats) => stats.Server.Applications.First(p => p.Name == "ingress")
|
||||||
long.Parse(stats.Server.Applications.First(p => p.Name == "ingress")
|
.MethodLive.Streams
|
||||||
.MethodLive.Streams.First(p => p.Name == user)
|
.Any(p => p.Name == user && p.Clients.Any(c => c.Address == target.Replace("rtmp://", "")));
|
||||||
.Time);
|
|
||||||
|
|
||||||
public static int CountLiveRestreams(string user, StatsObject stats) {
|
public static long GetClientTime(string user, StatsObject stats) =>
|
||||||
var db = new AppDb.DbConn();
|
long.Parse(stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.First(p => p.Name == user).Time);
|
||||||
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 int CountLiveRestreams(string user, string privateAccesskey, StatsObject stats) {
|
public static int CountLiveRestreams(string user, StatsObject stats) {
|
||||||
var db = new AppDb.DbConn();
|
var db = new AppDb.DbConn();
|
||||||
var dbUser = db.Users.First(p => p.Username == user);
|
var dbUser = db.Users.First(p => p.Username == user);
|
||||||
return dbUser.RestreamTargets.Split(",")
|
return dbUser.RestreamTargets.Split(",")
|
||||||
.Count(target => stats.Server.Applications.First(p => p.Name == "ingress")
|
.Count(target => stats.Server.Applications.First(p => p.Name == "ingress")
|
||||||
.MethodLive.Streams
|
.MethodLive.Streams.Any(p => p.Name == user && p.Clients.Any(c => c.Address == target.Replace("rtmp://", ""))));
|
||||||
.Any(p => p.Name == privateAccesskey
|
|
||||||
&& p.Clients.Any(c => c.Address
|
|
||||||
== target.Replace("rtmp://", ""))));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static List<string> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[XmlRoot(ElementName = "rtmp")]
|
public static int CountLiveRestreams(string user, string privateAccesskey, StatsObject stats) {
|
||||||
public class StatsObject {
|
var db = new AppDb.DbConn();
|
||||||
[XmlElement(ElementName = "nginx_version", IsNullable = true)]
|
var dbUser = db.Users.First(p => p.Username == user);
|
||||||
public string NginxVersion { get; set; }
|
return dbUser.RestreamTargets.Split(",")
|
||||||
|
.Count(target => stats.Server.Applications.First(p => p.Name == "ingress")
|
||||||
[XmlElement(ElementName = "nginx_rtmp_version", IsNullable = true)]
|
.MethodLive.Streams.Any(p => p.Name == privateAccesskey && p.Clients.Any(c => c.Address == target.Replace("rtmp://", ""))));
|
||||||
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 static List<string> ListLiveUsers() => GetStatsObject().Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.Select(p => p.Name).ToList();
|
||||||
public class Server {
|
|
||||||
[XmlElement(ElementName = "application", IsNullable = true)] public List<Application> Applications { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[XmlRoot(ElementName = "application", IsNullable = true)]
|
public static StatsObject GetStatsObject() {
|
||||||
public class Application {
|
var obj = (StatsObject)Serializer.Deserialize(new WebClient().OpenRead("http://127.0.0.1:8080")!);
|
||||||
[XmlElement(ElementName = "live", IsNullable = true)] public MethodLive MethodLive { get; set; }
|
return obj;
|
||||||
[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<Stream> 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<Client> 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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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<Application> 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<Stream> 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<Client> 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; }
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue