Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.Net6/Blazor sample app #205

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions BlazorTestApp/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
42 changes: 42 additions & 0 deletions BlazorTestApp/BlazorTestApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\OpenTok\OpenTok.csproj"/>
</ItemGroup>

<ItemGroup>
<Content Update="wwwroot\content\img\archiving-off.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\content\img\archiving-on-idle.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\content\img\archiving-on-message.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<_ContentIncludedByDefault Remove="Pages\Error.cshtml"/>
<_ContentIncludedByDefault Remove="Pages\_Host.cshtml"/>
<_ContentIncludedByDefault Remove="Pages\_Layout.cshtml"/>
<_ContentIncludedByDefault Remove="Pages\Archives.razor"/>
<_ContentIncludedByDefault Remove="Pages\Counter.razor"/>
<_ContentIncludedByDefault Remove="Pages\FetchData.razor"/>
<_ContentIncludedByDefault Remove="Pages\Host.razor"/>
<_ContentIncludedByDefault Remove="Pages\Index.razor"/>
<_ContentIncludedByDefault Remove="Pages\Participant.razor"/>
<_ContentIncludedByDefault Remove="Pages\ParticipantManagement\Participant.razor"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.0"/>
</ItemGroup>

</Project>
32 changes: 32 additions & 0 deletions BlazorTestApp/Data/IVideoService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using LanguageExt;
using OpenTokSDK;
using OpenTokSDK.Render;

namespace BlazorTestApp.Data;

public interface IVideoService
{
void CreateSession();

void AssignCredentials(SessionCredentials sessionCredentials);

Task DeleteArchiveAsync(Guid archiveId);

Task StopArchiveAsync(Guid archiveId);

Task<Archive> GetArchiveAsync(Guid archiveId);

Task<ArchiveList> ListArchivesAsync();

Task<Archive> StartArchiveAsync(string sessionId);

int GetApiKey();

Option<SessionCredentials> GetCredentials();

Option<SessionInformation> GetSessionInformation();

Task<Option<RenderItem>> StartExperienceComposerAsync(string url, string streamName);

Task StopExperienceComposerAsync(RenderItem rendering);
}
17 changes: 17 additions & 0 deletions BlazorTestApp/Data/OpenTokOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace BlazorTestApp.Data;

public class OpenTokOptions
{
public OpenTokOptions(string apiKey, string apiSecret)
{
this.ApiKey = apiKey;
this.ApiSecret = apiSecret;
}

public OpenTokOptions()
{
}

public string ApiKey { get; set; } = string.Empty;
public string ApiSecret { get; set; } = string.Empty;
}
18 changes: 18 additions & 0 deletions BlazorTestApp/Data/SessionCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using LanguageExt;
using OpenTokSDK;

namespace BlazorTestApp.Data;

public record SessionCredentials(string SessionId, string Token, bool IsNewSession)
{
public static Option<SessionCredentials> FromExistingSession(string sessionId, string token) =>
ParseCredentials(sessionId, token, true);

private static Option<SessionCredentials> ParseCredentials(string sessionId, string token, bool isNewSession) =>
string.IsNullOrEmpty(sessionId) && string.IsNullOrEmpty(token)
? Option<SessionCredentials>.None
: new SessionCredentials(sessionId, token, isNewSession);

public static Option<SessionCredentials> FromCreatedSession(Session session) =>
ParseCredentials(session.Id, session.GenerateToken(), false);
}
3 changes: 3 additions & 0 deletions BlazorTestApp/Data/SessionInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace BlazorTestApp.Data;

public record SessionInformation(SessionCredentials Credentials, string ApiKey);
66 changes: 66 additions & 0 deletions BlazorTestApp/Data/VideoService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Net;
using LanguageExt;
using OpenTokSDK;
using OpenTokSDK.Render;
using static LanguageExt.Prelude;

namespace BlazorTestApp.Data;

public class VideoService : IVideoService
{
private readonly OpenTok openTok;
private Option<SessionCredentials> credentials = Option<SessionCredentials>.None;

public VideoService(OpenTokOptions options)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
this.openTok = new OpenTok(Convert.ToInt32(options.ApiKey), options.ApiSecret);
}

public void CreateSession() =>
Some(this.openTok.CreateSession(mediaMode: MediaMode.ROUTED))
.Bind(SessionCredentials.FromCreatedSession)
.IfSome(this.AssignCredentials);

public void AssignCredentials(SessionCredentials sessionCredentials) => this.credentials = sessionCredentials;

public async Task DeleteArchiveAsync(Guid archiveId) => await this.openTok.DeleteArchiveAsync(archiveId.ToString());

public int GetApiKey() => this.openTok.ApiKey;

public Option<SessionCredentials> GetCredentials() => this.credentials;

public Option<SessionInformation> GetSessionInformation() => this.credentials
.Map(cred => new SessionInformation(cred, this.GetApiKey().ToString()));

public async Task<Option<RenderItem>> StartExperienceComposerAsync(string url, string streamName) =>
await this.GetCredentials()
.Map(session => CreateRenderingRequest(url, streamName, session))
.MapAsync(this.StartRenderAsync)
.Match(Some, () => Option<RenderItem>.None);

public async Task StopExperienceComposerAsync(RenderItem rendering) =>
await this.openTok.StopRenderAsync(rendering.Id);

public async Task StopArchiveAsync(Guid archiveId) => await this.openTok.StopArchiveAsync(archiveId.ToString());

public async Task<Archive> GetArchiveAsync(Guid archiveId) =>
await this.openTok.GetArchiveAsync(archiveId.ToString());

public async Task<ArchiveList> ListArchivesAsync() => await this.openTok.ListArchivesAsync();

public async Task<Archive> StartArchiveAsync(string sessionId) => await this.openTok.StartArchiveAsync(
sessionId,
"Blazor Sample App",
true,
true,
OutputMode.COMPOSED,
"1920x1080",
new ArchiveLayout {Type = LayoutType.bestFit, ScreenShareType = ScreenShareLayoutType.BestFit}
);

private Task<RenderItem> StartRenderAsync(StartRenderRequest request) => this.openTok.StartRenderAsync(request);

private static StartRenderRequest CreateRenderingRequest(string url, string streamName, SessionCredentials cred) =>
new(cred.SessionId, cred.Token, new Uri(url), new Uri(url), streamName, 60);
}
92 changes: 92 additions & 0 deletions BlazorTestApp/Pages/ArchivesManagement/Archives.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
@page "/archives"
@using BlazorTestApp.Data
@using OpenTokSDK
@using System.Globalization
@inject IVideoService Service
@inject NavigationManager NavigationManager
@inject IJSRuntime JavascriptRuntime
<PageTitle>Archives</PageTitle>

<div class="container bump-me">

<div class="body-content">

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Past Archives</h3>
</div>
<div class="panel-body">
@if (archiveCount >= 0)
{
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Created</th>
<th>Duration</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach (var archive in archives)
{
<tr data-item-id="@archive.Id">
<td>@archive.Name</td>
<td>@GetMinimumDate().AddMilliseconds(archive.CreatedAt).ToString(CultureInfo.InvariantCulture)</td>
<td>@archive.Duration seconds</td>
<td>@archive.Status</td>
<td>
@if (archive.Status == ArchiveStatus.EXPIRED)
{
@:Expired
}

@if (archive.Status == ArchiveStatus.AVAILABLE)
{
<button class="btn btn-primary" onclick="@(async () => await OpenArchiveAsync(archive.Id))">Open</button>
<button class="btn btn-danger" onclick="@(async () => await DeleteArchiveAsync(archive.Id))">Delete</button>
}
</td>
</tr>
}

</tbody>
</table>
}
else
{
<p>Loading archives...</p>
}
</div>
</div>
</div>
</div>

@code {

private IEnumerable<Archive> archives = new List<Archive>();
private int archiveCount = -1;

protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
var openTokArchives = await Service.ListArchivesAsync();
var filteredArchives = openTokArchives.Where(archive => archive.Status != ArchiveStatus.EXPIRED).ToList();
archiveCount = filteredArchives.Count;
archives = filteredArchives;
}

private async Task OpenArchiveAsync(Guid archiveId)
{
var archive = await Service.GetArchiveAsync(archiveId);
await JavascriptRuntime.InvokeVoidAsync("open", archive.Url, "_blank");
}

private async Task DeleteArchiveAsync(Guid archiveId)
{
await Service.DeleteArchiveAsync(archiveId);
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}

private static DateTime GetMinimumDate() => new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
}
42 changes: 42 additions & 0 deletions BlazorTestApp/Pages/Error.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@page
@model BlazorTestApp.Pages.ErrorModel

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet"/>
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true"/>
</head>

<body>
<div class="main">
<div class="content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</div>
</div>
</body>

</html>
26 changes: 26 additions & 0 deletions BlazorTestApp/Pages/Error.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace BlazorTestApp.Pages;

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
private readonly ILogger<ErrorModel> _logger;

public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}

public string? RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
Loading