├── SpawnDev.BlazorJS.PeerJS
├── _Imports.razor
├── Assets
│ └── icon-128.png
├── AnswerOptions.cs
├── CallOptions.cs
├── PeerError.cs
├── MediaConnection.cs
├── ConnectOptions.cs
├── SpawnDev.BlazorJS.PeerJS.csproj
├── PeerConnection.cs
├── DataConnection.cs
├── PeerOptions.cs
├── PeerConnectionSet.cs
├── WebPeer.cs
├── EventEmitter.cs
├── Peer.cs
└── wwwroot
│ └── peerjs.min.js
├── SpawnDev.BlazorJS.PeerJS.Demo
├── wwwroot
│ ├── favicon.png
│ ├── icon-192.png
│ ├── index.html
│ └── css
│ │ └── app.css
├── Layout
│ ├── MainLayout.razor
│ ├── MainLayout.razor.css
│ ├── NavMenu.razor
│ └── NavMenu.razor.css
├── _Imports.razor
├── App.razor
├── Program.cs
├── SpawnDev.BlazorJS.PeerJS.Demo.csproj
├── Properties
│ └── launchSettings.json
└── Pages
│ ├── WebPeerExample.razor
│ └── ConnectExample.razor
├── .github
├── FUNDING.yml
└── workflows
│ └── main.yml
├── LICENSE.txt
├── SpawnDev.BlazorJS.PeerJS.sln
├── .gitattributes
├── .gitignore
└── README.md
/SpawnDev.BlazorJS.PeerJS/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components.Web
2 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/Assets/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.PeerJS/main/SpawnDev.BlazorJS.PeerJS/Assets/icon-128.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.PeerJS/main/SpawnDev.BlazorJS.PeerJS.Demo/wwwroot/favicon.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.PeerJS/main/SpawnDev.BlazorJS.PeerJS.Demo/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
6 |
7 |
8 |
9 | @Body
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 | @using SpawnDev.BlazorJS.PeerJS.Demo
10 | @using SpawnDev.BlazorJS.PeerJS.Demo.Layout
11 | @using SpawnDev.Blazor.QRCodeRenderer
12 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 | Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components.Web;
2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3 | using SpawnDev.BlazorJS;
4 | using SpawnDev.BlazorJS.PeerJS.Demo;
5 |
6 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
7 | builder.RootComponents.Add("#app");
8 | builder.RootComponents.Add("head::after");
9 | // Add SpawnDev.BlazorJS interop
10 | builder.Services.AddBlazorJSRuntime();
11 | // Run app using BlazorJSRunAsync
12 | await builder.Build().BlazorJSRunAsync();
13 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/AnswerOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace SpawnDev.BlazorJS.PeerJS
4 | {
5 | ///
6 | /// Options used for MediaConnection.Answer
7 | /// https://peerjs.com/docs/#mediaconnection-answer-options
8 | ///
9 | public class AnswerOptions
10 | {
11 | ///
12 | /// Function which runs before create answer to modify sdp answer message.
13 | ///
14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
15 | public FuncCallback? SdpTransform { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/SpawnDev.BlazorJS.PeerJS.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [LostBeard] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
15 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/CallOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace SpawnDev.BlazorJS.PeerJS
4 | {
5 | ///
6 | /// Options used for Peer.Call
7 | /// https://peerjs.com/docs/#peercall-options
8 | ///
9 | public class CallOptions
10 | {
11 | ///
12 | /// Metadata associated with the connection, passed in by whoever initiated the connection. Can be accessed with mediaConnection.metadata. Can be any serializable type.
13 | ///
14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
15 | public object? Metadata { get; set; }
16 | ///
17 | /// Function which runs before create offer to modify sdp offer message.
18 | ///
19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
20 | public FuncCallback? SdpTransform { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/PeerError.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.PeerJS
5 | {
6 | ///
7 | /// A PeerJS Error object
8 | /// https://nodejs.org/api/errors.html#class-error
9 | ///
10 | public class PeerError : Error
11 | {
12 | ///
13 | /// Deserialization constructor
14 | ///
15 | ///
16 | public PeerError(IJSInProcessObjectReference _ref) : base(_ref) { }
17 | ///
18 | /// The error.type property is a string label that identifies the kind of error. error.type is the most stable way to identify an error. It will only change between major versions of Node.js. In contrast, error.message strings may change between any versions of Node.js. See Node.js error codes for details about specific codes.
19 | ///
20 | public string? Type => JSRef!.Get("type");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SpawnDev.BlazorJS.PeerJS.Demo
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 | An unhandled error has occurred.
26 |
Reload
27 |
🗙
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:25714",
8 | "sslPort": 44307
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
17 | "applicationUrl": "http://localhost:5084",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
27 | "applicationUrl": "https://localhost:7039;http://localhost:5192",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/MediaConnection.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.PeerJS
5 | {
6 | ///
7 | /// Wraps WebRTC's media streams. To get one, use peer.call or listen for the call event.
8 | /// https://peerjs.com/docs/#mediaconnection
9 | ///
10 | public class MediaConnection : PeerConnection
11 | {
12 | ///
13 | /// The value used for connections of this type
14 | ///
15 | public static string ConnectionType { get; } = "media";
16 | ///
17 | /// Deserialization constructor
18 | ///
19 | ///
20 | public MediaConnection(IJSInProcessObjectReference _ref) : base(_ref) { }
21 | ///
22 | /// When receiving a call event on a peer, you can call .answer on the media connection provided by the callback to accept the call and optionally send your own media stream.
23 | ///
24 | /// A WebRTC media stream from getUserMedia.
25 | /// Function which runs before create answer to modify sdp answer message.
26 | public void Answer(MediaStream? stream = null, AnswerOptions? options = null) => JSRef!.CallVoid("answer", stream);
27 | ///
28 | /// Emitted when a remote peer adds a stream.
29 | ///
30 | public JSEventCallback OnStream { get => new JSEventCallback("stream", On, Off); set { } }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Layout/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row ::deep .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | text-decoration: none;
28 | }
29 |
30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .top-row ::deep a:first-child {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 |
39 | @media (max-width: 640.98px) {
40 | .top-row {
41 | justify-content: space-between;
42 | }
43 |
44 | .top-row ::deep a, .top-row ::deep .btn-link {
45 | margin-left: 0;
46 | }
47 | }
48 |
49 | @media (min-width: 641px) {
50 | .page {
51 | flex-direction: row;
52 | }
53 |
54 | .sidebar {
55 | width: 250px;
56 | height: 100vh;
57 | position: sticky;
58 | top: 0;
59 | }
60 |
61 | .top-row {
62 | position: sticky;
63 | top: 0;
64 | z-index: 1;
65 | }
66 |
67 | .top-row.auth ::deep a:first-child {
68 | flex: 1;
69 | text-align: right;
70 | width: 0;
71 | }
72 |
73 | .top-row, article {
74 | padding-left: 2rem !important;
75 | padding-right: 1.5rem !important;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.34929.205
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnDev.BlazorJS.PeerJS", "SpawnDev.BlazorJS.PeerJS\SpawnDev.BlazorJS.PeerJS.csproj", "{0CB15D4D-1907-41CA-AE64-E641B2625B63}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnDev.BlazorJS.PeerJS.Demo", "SpawnDev.BlazorJS.PeerJS.Demo\SpawnDev.BlazorJS.PeerJS.Demo.csproj", "{87422002-F886-44A1-B586-FB3B10170C8E}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {0CB15D4D-1907-41CA-AE64-E641B2625B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {0CB15D4D-1907-41CA-AE64-E641B2625B63}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {0CB15D4D-1907-41CA-AE64-E641B2625B63}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {0CB15D4D-1907-41CA-AE64-E641B2625B63}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {87422002-F886-44A1-B586-FB3B10170C8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {87422002-F886-44A1-B586-FB3B10170C8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {87422002-F886-44A1-B586-FB3B10170C8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {87422002-F886-44A1-B586-FB3B10170C8E}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3D75FBB1-8A2C-400F-AC9B-D2886B593076}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/ConnectOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace SpawnDev.BlazorJS.PeerJS
4 | {
5 | ///
6 | /// Peer.Connect options
7 | /// https://peerjs.com/docs/#peerconnect-options
8 | ///
9 | public class ConnectOptions
10 | {
11 | ///
12 | /// A unique label by which you want to identify this data connection. If left unspecified, a label will be generated at random. Can be accessed with dataConnection.label.
13 | ///
14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
15 | public string? Label { get; set; }
16 | ///
17 | /// Metadata associated with the connection, passed in by whoever initiated the connection. Can be accessed with dataConnection.metadata. Can be any serializable type.
18 | ///
19 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
20 | public object? Metadata { get; set; }
21 | ///
22 | /// Can be binary (default), binary-utf8, json, or none. Can be accessed with dataConnection.serialization.
23 | /// binary-utf8 will take a performance hit because of the way UTF8 strings are packed into binary format.
24 | ///
25 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
26 | public string? Serialization { get; set; }
27 | ///
28 | /// Whether the underlying data channels should be reliable (e.g. for large file transfers) or not (e.g. for gaming or streaming). Defaults to false.
29 | /// Setting reliable to true will use a shim for incompatible browsers (Chrome 30 and below only) and thus may not offer full performance.
30 | ///
31 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
32 | public bool? Reliable { get; set; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | # Run workflow on every push to the master branch
4 | on: workflow_dispatch
5 |
6 | jobs:
7 | deploy-to-github-pages:
8 | permissions:
9 | contents: write
10 | # use ubuntu-latest image to run steps on
11 | runs-on: ubuntu-latest
12 | steps:
13 | # uses GitHub's checkout action to checkout code form the master branch
14 | - uses: actions/checkout@v2
15 |
16 | # sets up .NET Core SDK
17 | - name: Setup .NET Core SDK
18 | uses: actions/setup-dotnet@v3.0.3
19 | with:
20 | dotnet-version: 8.0.400-preview.0.24324.5
21 |
22 | # Install dotnet wasm buildtools workload
23 | - name: Install .NET WASM Build Tools
24 | run: dotnet workload install wasm-tools
25 |
26 | # publishes Blazor project to the publish-folder
27 | - name: Publish .NET Core Project
28 | run: dotnet publish ./SpawnDev.BlazorJS.PeerJS.Demo/ --nologo -c:Release --output publish
29 |
30 | # changes the base-tag in index.html from '/' to '/SpawnDev.BlazorJS.PeerJS/' to match GitHub Pages repository subdirectory
31 | - name: Change base-tag in index.html from / to /SpawnDev.BlazorJS.PeerJS/
32 | run: sed -i 's/
2 |
3 |
4 | net6.0;net7.0;net8.0;net9.0;net10.0
5 | enable
6 | enable
7 | 1.7.0
8 | True
9 | true
10 | true
11 | Embedded
12 | SpawnDev.BlazorJS.PeerJS
13 | LostBeard
14 | PeerJS simplifies peer-to-peer data, video, and audio calls in Blazor WebAssembly
15 | https://github.com/LostBeard/SpawnDev.BlazorJS.PeerJS
16 | README.md
17 | LICENSE.txt
18 | icon-128.png
19 | https://github.com/LostBeard/SpawnDev.BlazorJS.PeerJS.git
20 | git
21 | Blazor;BlazorWebAssembly;PeerJS;WebRTC
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 | false
45 | false
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/PeerConnection.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace SpawnDev.BlazorJS.PeerJS
4 | {
5 | ///
6 | /// Base class for MediaConnection and DataConnection
7 | ///
8 | public class PeerConnection : EventEmitter
9 | {
10 | ///
11 | /// Deserialization constructor
12 | ///
13 | ///
14 | public PeerConnection(IJSInProcessObjectReference _ref) : base(_ref) { }
15 | ///
16 | /// For media connections, this is always 'media'.
17 | ///
18 | public string Type => JSRef!.Get("type");
19 | ///
20 | /// Any type of metadata associated with the connection, passed in by whoever initiated the connection.
21 | ///
22 | public JSObject? Metadata => JSRef!.Get("metadata");
23 | ///
24 | /// Any type of metadata associated with the connection, passed in by whoever initiated the connection.
25 | ///
26 | ///
27 | ///
28 | public T MetadataAs() => JSRef!.Get("metadata");
29 | ///
30 | /// Closes the media connection.
31 | ///
32 | public void Close() => JSRef!.CallVoid("close");
33 | ///
34 | /// Whether the media connection is active (e.g. your call has been answered). You can check this if you want to set a maximum wait time for a one-sided call.
35 | ///
36 | public bool Open => JSRef!.Get("open");
37 | ///
38 | /// The ID of the peer on the other end of this connection.
39 | ///
40 | public string Peer => JSRef!.Get("peer");
41 | ///
42 | /// Emitted when either you or the remote peer closes the data connection.
43 | ///
44 | public JSEventCallback OnClose { get => new JSEventCallback("close", On, Off); set { } }
45 | ///
46 | /// Emitted when an error occurs
47 | ///
48 | public JSEventCallback OnError { get => new JSEventCallback("error", On, Off); set { } }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Layout/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
33 |
34 | @code {
35 | private bool collapseNavMenu = true;
36 |
37 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
38 |
39 | private void ToggleNavMenu()
40 | {
41 | collapseNavMenu = !collapseNavMenu;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS.Demo/Layout/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | background-color: rgba(255, 255, 255, 0.1);
3 | }
4 |
5 | .top-row {
6 | height: 3.5rem;
7 | background-color: rgba(0,0,0,0.4);
8 | }
9 |
10 | .navbar-brand {
11 | font-size: 1.1rem;
12 | }
13 |
14 | .bi {
15 | display: inline-block;
16 | position: relative;
17 | width: 1.25rem;
18 | height: 1.25rem;
19 | margin-right: 0.75rem;
20 | top: -1px;
21 | background-size: cover;
22 | }
23 |
24 | .bi-house-door-fill-nav-menu {
25 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
26 | }
27 |
28 | .bi-plus-square-fill-nav-menu {
29 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
30 | }
31 |
32 | .bi-list-nested-nav-menu {
33 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
34 | }
35 |
36 | .nav-item {
37 | font-size: 0.9rem;
38 | padding-bottom: 0.5rem;
39 | }
40 |
41 | .nav-item:first-of-type {
42 | padding-top: 1rem;
43 | }
44 |
45 | .nav-item:last-of-type {
46 | padding-bottom: 1rem;
47 | }
48 |
49 | .nav-item ::deep a {
50 | color: #d7d7d7;
51 | border-radius: 4px;
52 | height: 3rem;
53 | display: flex;
54 | align-items: center;
55 | line-height: 3rem;
56 | }
57 |
58 | .nav-item ::deep a.active {
59 | background-color: rgba(255,255,255,0.37);
60 | color: white;
61 | }
62 |
63 | .nav-item ::deep a:hover {
64 | background-color: rgba(255,255,255,0.1);
65 | color: white;
66 | }
67 |
68 | @media (min-width: 641px) {
69 | .navbar-toggler {
70 | display: none;
71 | }
72 |
73 | .collapse {
74 | /* Never collapse the sidebar for wide screens */
75 | display: block;
76 | }
77 |
78 | .nav-scrollable {
79 | /* Allow sidebar to scroll for tall menus */
80 | height: calc(100vh - 3.5rem);
81 | overflow-y: auto;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/DataConnection.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects.WebRTC;
3 |
4 | namespace SpawnDev.BlazorJS.PeerJS
5 | {
6 | ///
7 | /// Wraps WebRTC's DataChannel. To get one, use peer.connect or listen for the connect event.
8 | /// https://peerjs.com/docs/#dataconnection
9 | ///
10 | public class DataConnection : PeerConnection
11 | {
12 | ///
13 | /// The value used for connections of this type
14 | ///
15 | public static string ConnectionType { get; } = "data";
16 | ///
17 | /// Deserialization constructor
18 | ///
19 | ///
20 | public DataConnection(IJSInProcessObjectReference _ref) : base(_ref) { }
21 |
22 | ///
23 | /// data is serialized by BinaryPack by default and sent to the remote peer.
24 | ///
25 | /// You can send any type of data, including objects, strings, and blobs.
26 | public void Send(object data) => JSRef!.CallVoid("send", data);
27 |
28 | ///
29 | /// A reference to the RTCDataChannel object associated with the connection.
30 | ///
31 | public RTCDataChannel DataChannel => JSRef!.Get("dataChannel");
32 | ///
33 | /// The optional label passed in or assigned by PeerJS when the connection was initiated.
34 | ///
35 | public string Label => JSRef!.Get("label");
36 | ///
37 | /// A reference to the RTCPeerConnection object associated with the connection.
38 | ///
39 | public RTCPeerConnection PeerConnection => JSRef!.Get("peerConnection");
40 | ///
41 | /// Whether the underlying data channels are reliable; defined when the connection was initiated.
42 | ///
43 | public bool Reliable => JSRef!.Get("reliable");
44 | ///
45 | /// The serialization format of the data sent over the connection. Can be binary (default), binary-utf8, json, or none.
46 | ///
47 | public string Serialization => JSRef!.Get("serialization");
48 | ///
49 | /// The number of messages queued to be sent once the browser buffer is no longer full.
50 | ///
51 | public int BufferSize=> JSRef!.Get("bufferSize");
52 |
53 | ///
54 | /// Emitted when data is received from the remote peer.
55 | ///
56 | public JSEventCallback OnData{ get => new JSEventCallback("data", On, Off); set { } }
57 | ///
58 | /// Emitted when the connection is established and ready-to-use.
59 | ///
60 | public JSEventCallback OnOpen { get => new JSEventCallback("open", On, Off); set { } }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/PeerOptions.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects.WebRTC;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace SpawnDev.BlazorJS.PeerJS
5 | {
6 | ///
7 | /// Options used when creating a new Peer
8 | /// https://peerjs.com/docs/#peer-options
9 | ///
10 | public class PeerOptions
11 | {
12 | ///
13 | /// Optional token that can be used for authentication on the PeerServer
14 | /// peerServer.on('connection', (client) => { console.log('client.token:', client.token); })
15 | ///
16 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
17 | public string? Token { get; set; }
18 | ///
19 | /// API key for the cloud PeerServer. This is not used for servers other than 0.peerjs.com.
20 | /// PeerServer cloud runs on port 443. Please ensure it is not blocked or consider running your own PeerServer instead.
21 | ///
22 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
23 | public string? Key { get; set; }
24 | ///
25 | /// Server host. Defaults to 0.peerjs.com. Also accepts '/' to signify relative hostname.
26 | ///
27 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
28 | public string? Host { get; set; }
29 | ///
30 | /// Server port. Defaults to 443.
31 | ///
32 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
33 | public ushort? Port { get; set; }
34 | ///
35 | /// Ping interval in ms. Defaults to 5000.
36 | ///
37 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
38 | public int? PingInterval { get; set; }
39 | ///
40 | /// The path where your self-hosted PeerServer is running. Defaults to '/'.
41 | ///
42 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
43 | public string? Path { get; set; }
44 | ///
45 | /// true if you're using SSL.
46 | /// Note that our cloud-hosted server and assets may not support SSL.
47 | ///
48 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
49 | public bool? Secure { get; set; }
50 | ///
51 | /// Configuration hash passed to RTCPeerConnection. This hash contains any custom ICE/TURN server configuration. Defaults to { 'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }], 'sdpSemantics': 'unified-plan' }
52 | ///
53 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
54 | public RTCConfiguration? Config { get; set; }
55 | ///
56 | /// Prints log messages depending on the debug level passed in. Defaults to 0.
57 | /// 0 Prints no logs.
58 | /// 1 Prints only errors.
59 | /// 2 Prints errors and warnings.
60 | /// 3 Prints all logs.
61 | ///
62 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
63 | public int? Debug { get; set; }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/PeerConnectionSet.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace SpawnDev.BlazorJS.PeerJS
4 | {
5 | ///
6 | /// This class provides access to Peer.Connections
7 | ///
8 | public class PeerConnectionSet : JSObject
9 | {
10 | ///
11 | /// Deserialization constructor
12 | ///
13 | ///
14 | public PeerConnectionSet(IJSInProcessObjectReference _ref) : base(_ref) { }
15 | ///
16 | /// Returns peer ids
17 | ///
18 | public string[] Ids => JS.Call("Object.keys", JSRef);
19 | ///
20 | /// Returns the connections for a given peer id
21 | ///
22 | ///
23 | ///
24 | public PeerConnection[]? GetPeerConnections(string id) => JSRef!.Get(id);
25 | ///
26 | /// Returns all DataConnection associated with this peer id
27 | ///
28 | ///
29 | ///
30 | public DataConnection[]? GetDataConnections(string id) => GetPeerConnections(id, DataConnection.ConnectionType);
31 | ///
32 | /// Returns all MediaConnections associated with this peer id
33 | ///
34 | ///
35 | ///
36 | public MediaConnection[]? GetMediaConnections(string id) => GetPeerConnections(id, MediaConnection.ConnectionType);
37 | ///
38 | /// Returns all PeerConnections of the given type associated with this peer id
39 | ///
40 | ///
41 | ///
42 | ///
43 | ///
44 | private TConnection[]? GetPeerConnections(string id, string type) where TConnection : PeerConnection
45 | {
46 | var connections = GetPeerConnections(id);
47 | if (connections == null) return null;
48 | var ret = new List();
49 | foreach (var connection in connections)
50 | {
51 | if (connection.Type != type)
52 | {
53 | connection.Dispose();
54 | }
55 | else
56 | {
57 | ret.Add(connection.JSRefMove());
58 | }
59 | }
60 | return ret.ToArray();
61 | }
62 | ///
63 | /// Returns a dictionary of all MediaConnections keyed by peer id
64 | ///
65 | ///
66 | public Dictionary GetMediaConnections() => GetConnections(MediaConnection.ConnectionType);
67 | ///
68 | /// Returns a dictionary of all DataConnections keyed by peer id
69 | ///
70 | ///
71 | public Dictionary GetDataConnections() => GetConnections(DataConnection.ConnectionType);
72 | private Dictionary GetConnections(string type) where TConnection : PeerConnection
73 | {
74 | var ret = new Dictionary();
75 | foreach (var id in Ids)
76 | {
77 | var conns = GetPeerConnections(id, type)!;
78 | if (conns == null) continue;
79 | ret[id] = conns;
80 | }
81 | return ret;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.PeerJS/WebPeer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using SpawnDev.BlazorJS.WebWorkers;
3 | using System.Reflection;
4 | using Array = SpawnDev.BlazorJS.JSObjects.Array;
5 |
6 | namespace SpawnDev.BlazorJS.PeerJS
7 | {
8 | public class WebPeer : RemoteDispatcher
9 | {
10 | public Peer Peer { get; private set; }
11 | public DataConnection? DataConnection { get; private set; } = null;
12 | public WebPeer(IServiceProvider serviceProvider, Peer peer, DataConnection dataConnection) : base(serviceProvider)
13 | {
14 | Peer = peer;
15 | InitDataConnection(dataConnection);
16 | }
17 | public WebPeer(IServiceProvider serviceProvider, Peer peer, string targetId, ConnectOptions? options = null) : base(serviceProvider)
18 | {
19 | Peer = peer;
20 | InitDataConnection(Peer.Connect(targetId, options));
21 | }
22 | protected override void SendCall(object?[] args) => DataConnection!.Send(args);
23 | private void InitDataConnection(DataConnection dataConnection)
24 | {
25 | DataConnection = dataConnection;
26 | DataConnection.OnOpen += DataConnection_OnOpen;
27 | DataConnection.OnClose += DataConnection_OnClose;
28 | DataConnection.OnError += DataConnection_OnError;
29 | DataConnection.On("data", DataConnection_OnData);
30 | if (DataConnection.Open)
31 | {
32 | try
33 | {
34 | SendReadyFlag();
35 | }
36 | catch (Exception ex)
37 | {
38 | Console.WriteLine($"SendReadyFlag failed: {ex.Message}");
39 | }
40 | }
41 | }
42 | private void DataConnection_OnData(Array msg) => _ = HandleCall(msg);
43 | private void DataConnection_OnOpen()
44 | {
45 | //Log("DataConnection_OnOpen");
46 | //Send($"Hello from {id}");
47 | try
48 | {
49 | SendReadyFlag();
50 | }
51 | catch (Exception ex)
52 | {
53 | Console.WriteLine($"SendReadyFlag failed: {ex.Message}");
54 | }
55 | }
56 | private void DataConnection_OnClose()
57 | {
58 | JS.Log("DataConnection_OnClose");
59 | //DisposeDataConnection();
60 | ResetWhenReady();
61 | }
62 | private void DataConnection_OnError(PeerError error)
63 | {
64 | //Log($"DataConnection_OnError: {error.Type}");
65 | }
66 | public override void Dispose()
67 | {
68 | if (IsDisposed) return;
69 | if (DataConnection != null)
70 | {
71 | DataConnection.OnOpen -= DataConnection_OnOpen;
72 | DataConnection.OnClose -= DataConnection_OnClose;
73 | DataConnection.OnError -= DataConnection_OnError;
74 | DataConnection.Off("data", DataConnection_OnData);
75 | DataConnection.Dispose();
76 | }
77 | base.Dispose();
78 | }
79 | protected override Task CanCallCheck(MethodInfo methodInfo, RemoteCallableAttribute? remoteCallableAttr, ServiceDescriptor? info, object? instance)
80 | {
81 | return base.CanCallCheck(methodInfo, remoteCallableAttr, info, instance);
82 | }
83 | protected override Task PreCallCheck(MethodInfo methodInfo, object?[]? args = null)
84 | {
85 | return base.PreCallCheck(methodInfo, args);
86 | }
87 | protected override Task