Skip to content

Commit f1120be

Browse files
Merge pull request #101 from Community-Toolkit-for-Fluent-UI-Blazor/feat/fluentcx-video
Adds a FluentCxVideo, reworking FluentCxAudio and FluentCxObserverProvider
2 parents 1a823b5 + f16a6b9 commit f1120be

167 files changed

Lines changed: 9333 additions & 1319 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Directory.Packages.props

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
<!-- For Sample Apps -->
88
<PackageVersion Include="AWSSDK.S3" Version="4.0.7.6" />
99
<PackageVersion Include="Azure.Storage.Blobs" Version="12.25.1" />
10+
<PackageVersion Include="FFmpegBlazor" Version="1.0.0.10" />
11+
<PackageVersion Include="FFMpegCore" Version="5.4.0" />
1012
<PackageVersion Include="Google.Cloud.Storage.V1" Version="4.13.0" />
1113

1214
<PackageVersion Include="Markdig.Signed" Version="0.43.0" />
15+
<PackageVersion Include="MetadataExtractor" Version="2.9.0" />
1316
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.10" />
1417
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.10" />
1518
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.10" />
@@ -29,7 +32,8 @@
2932
<PackageVersion Include="Svg.Skia" Version="3.2.1" />
3033

3134
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
32-
35+
<PackageVersion Include="Xabe.FFmpeg" Version="6.0.2" />
36+
<PackageVersion Include="Xabe.FFmpeg.Downloader" Version="6.0.2" />
3337
<PackageVersion Include="xunit" Version="2.9.3" />
3438
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5">
3539
<PrivateAssets>all</PrivateAssets>
@@ -44,4 +48,4 @@
4448
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.13.1" />
4549
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.13.1" />
4650
</ItemGroup>
47-
</Project>
51+
</Project>

examples/demo/FluentUI.Demo.Shared/FluentUI.Blazor.Community.Components.xml

Lines changed: 3540 additions & 925 deletions
Large diffs are not rendered by default.

examples/demo/FluentUI.Demo.Shared/FluentUI.Demo.Shared.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<ItemGroup>
2929
<PackageReference Include="Markdig.Signed" />
3030
<PackageReference Include="Microsoft.AspNetCore.Components.Web" />
31+
<PackageReference Include="Xabe.FFmpeg.Downloader" />
3132
</ItemGroup>
3233

3334
<ItemGroup>

examples/demo/FluentUI.Demo.Shared/Layout/DemoNavProvider.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ public DemoNavProvider()
170170
icon: new Icons.Regular.Size20.Color(),
171171
title: "Color palette"
172172
),
173+
new NavLink(
174+
href: "video",
175+
icon: new Icons.Regular.Size20.Video(),
176+
title: "Video"
177+
),
173178
]),
174179
new NavGroup(
175180
icon: new Icons.Regular.Size20.Beaker(),

examples/demo/FluentUI.Demo.Shared/Pages/Audio/AudioPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
<ApiDocumentation Component="typeof(FluentCxAudio)" />
4545
<ApiDocumentation Component="typeof(AudioTrackItem)" />
4646
<ApiDocumentation Component="typeof(MinimalPlayer)" />
47-
<ApiDocumentation Component="typeof(TrackInfo)" />
47+
<ApiDocumentation Component="typeof(AudioTrackInfo)" />
4848
<ApiDocumentation Component="typeof(AudioTitleScroller)" />
4949
<ApiDocumentation Component="typeof(CompactPlayer)" />
5050
<ApiDocumentation Component="typeof(AudioVisualizer)" />
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
@using Microsoft.AspNetCore.StaticFiles
2+
@using Microsoft.JSInterop
3+
@implements IDisposable
4+
@inject IVideoMetadataProvider VideoMetadataProvider
5+
6+
<FluentMessageBar Intent="MessageIntent.Warning" AllowDismiss="false">
7+
The <c>VideoMetadataProvider</c> uses FFmpeg to extract metadata from video files.
8+
This feature is only enabled when the app runs on server. If you run the app in WebAssembly, the metadata extraction will be skipped.
9+
</FluentMessageBar>
10+
11+
<div style="height: 600px">
12+
<FluentStack Style="height: 100%">
13+
<FluentStack Orientation="Orientation.Vertical">
14+
<FluentSelect Items="@(Enum.GetValues<VideoPlayerView>())"
15+
@bind-SelectedOption="_view" />
16+
17+
<FluentInputFile Id="my-file-uploader"
18+
Mode="InputFileMode.SaveToTemporaryFolder"
19+
Multiple="true"
20+
MaximumFileCount="10"
21+
MaximumFileSize="@(250*1024*1024)"
22+
Accept="video/*"
23+
@bind-ProgressPercent="@ProgressPercent"
24+
OnCompleted="@OnCompletedAsync"
25+
Style="height: 200px;width:180px">
26+
<ChildContent>
27+
<label for="my-file-uploader">
28+
<FluentIcon Value="@(new @Icons.Regular.Size24.ArrowUpload())" />
29+
</label>
30+
31+
<div>
32+
Drag files here you wish to upload,
33+
or <label for="my-file-uploader">browse</label>
34+
for them<span style="color: red;">*</span>.
35+
<br />
36+
<em>Maximum of 10 files allowed.</em>
37+
</div>
38+
</ChildContent>
39+
</FluentInputFile>
40+
</FluentStack>
41+
42+
<FluentCxVideo @ref="_video"
43+
@bind-View="@_view"
44+
Subtitles="@_subtitleEntries" >
45+
@foreach (var item in _videoFiles)
46+
{
47+
<VideoTrackItem Source="@GetPath(item.Value)"
48+
Metadata="@_metadataCache[item.Key]" />
49+
}
50+
</FluentCxVideo>
51+
</FluentStack>
52+
</div>
53+
54+
@code {
55+
private bool IsHorizontal { get; set; } = true;
56+
private Orientation Orientation => IsHorizontal ? Orientation.Horizontal : Orientation.Vertical;
57+
private VideoPlayerView _view = VideoPlayerView.Default;
58+
private FluentCxVideo? _video;
59+
60+
private readonly Dictionary<string, string> _videoFiles = [];
61+
private readonly List<FluentInputFileEventArgs> _files = [];
62+
private readonly Dictionary<string, VideoMetadata> _metadataCache = [];
63+
private readonly MultilingualSubtitles _subtitleEntries = new();
64+
65+
[Inject]
66+
private IJSRuntime Runtime { get; set; } = null!;
67+
68+
int ProgressPercent = 0;
69+
70+
protected override void OnInitialized()
71+
{
72+
base.OnInitialized();
73+
74+
_subtitleEntries.AddRange((new()
75+
{
76+
Code = "en",
77+
Name = "English",
78+
},
79+
[
80+
new SubtitleEntry
81+
{
82+
Start = 1,
83+
End = 4,
84+
Text = "This is an example of a subtitle entry."
85+
},
86+
new SubtitleEntry
87+
{
88+
Start = 5,
89+
End = 8,
90+
Text = "You can add multiple entries with different timings."
91+
},
92+
new SubtitleEntry
93+
{
94+
Start = 9,
95+
End = 12,
96+
Text = "Subtitles enhance accessibility and user experience."
97+
}
98+
]
99+
),
100+
(new()
101+
{
102+
Code = "fr",
103+
Name = "Français",
104+
},
105+
[
106+
new SubtitleEntry
107+
{
108+
Start = 1,
109+
End = 4,
110+
Text = "Ceci est un exemple de sous-titre."
111+
},
112+
new SubtitleEntry
113+
{
114+
Start = 5,
115+
End = 8,
116+
Text = "Vous pouvez ajouter de multiple entrées avec un timing différent.."
117+
},
118+
new SubtitleEntry
119+
{
120+
Start = 9,
121+
End = 12,
122+
Text = "Les sous-titres peuvent améliorer l'accessibilité et l'expérience utilisateur."
123+
}
124+
]
125+
));
126+
}
127+
128+
private static string GetPath(string path)
129+
{
130+
if (path.StartsWith("blob"))
131+
{
132+
return path;
133+
}
134+
135+
var index = path.IndexOf("uploads");
136+
137+
if (index >= 0)
138+
{
139+
return path[(index - 1)..].Replace("\\", "/");
140+
}
141+
142+
return path.Replace(Directory.GetCurrentDirectory(), string.Empty)
143+
.Replace("wwwroot", string.Empty)
144+
.Replace("\\", "/")
145+
.Substring(2);
146+
}
147+
148+
private async Task OnCompletedAsync(IEnumerable<FluentInputFileEventArgs> files)
149+
{
150+
foreach (var file in files)
151+
{
152+
var newFileName = $"RenamedFile_{Guid.NewGuid()}{Path.GetExtension(file.Name)}";
153+
var filePath = newFileName;
154+
155+
if (System.OperatingSystem.IsBrowser())
156+
{
157+
var provider = new FileExtensionContentTypeProvider();
158+
if (!provider.TryGetContentType(file.Name, out var contentType))
159+
{
160+
contentType = "application/octet-stream";
161+
}
162+
163+
filePath = await Runtime.InvokeAsync<string>("getBlobUrl", contentType, File.ReadAllBytes(file.LocalFile!.FullName));
164+
}
165+
else
166+
{
167+
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", newFileName);
168+
var directory = Path.GetDirectoryName(filePath);
169+
if (!Directory.Exists(directory))
170+
{
171+
Directory.CreateDirectory(directory!);
172+
}
173+
174+
await using var stream = File.OpenRead(file.LocalFile!.FullName);
175+
await using var fileStream = File.Create(filePath);
176+
await stream.CopyToAsync(fileStream);
177+
}
178+
179+
if (!_videoFiles.ContainsKey(file.Name))
180+
{
181+
_videoFiles.Add(file.Name, filePath);
182+
}
183+
184+
if (!_metadataCache.ContainsKey(file.Name))
185+
{
186+
using var ms = new MemoryStream(File.ReadAllBytes(file.LocalFile.FullName));
187+
var metadata = await VideoMetadataProvider.GetFromStreamAsync(ms);
188+
_metadataCache.Add(file.Name, metadata ?? new());
189+
}
190+
191+
if (!_files.Contains(file))
192+
{
193+
_files.Add(file);
194+
}
195+
}
196+
197+
await Task.Delay(500);
198+
ProgressPercent = 0;
199+
}
200+
201+
public void Dispose()
202+
{
203+
try
204+
{
205+
foreach (var file in _videoFiles)
206+
{
207+
if (File.Exists(file.Value))
208+
{
209+
File.Delete(file.Value);
210+
}
211+
}
212+
213+
foreach (var file in _files)
214+
{
215+
if (file.LocalFile is not null && File.Exists(file.LocalFile.FullName))
216+
{
217+
File.Delete(file.LocalFile.FullName);
218+
}
219+
}
220+
}
221+
catch { }
222+
}
223+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@page "/video"
2+
@using FluentUI.Demo.Shared.Pages.Video.Examples
3+
4+
<h1>Video</h1>
5+
6+
<p>
7+
The <code>FluentCxVideo</code> component allows you to play video files in your Blazor application.
8+
</p>
9+
10+
<h2 id="example">Examples</h2>
11+
12+
<DemoSection Component="typeof(VideoDefault)" Title="Default">
13+
<Description>
14+
15+
</Description>
16+
</DemoSection>
17+
18+
<h2 id="documentation">Documentation</h2>
19+
20+
<ApiDocumentation Component="typeof(ChapterList)" />

src/Community.Components/Components/Animations/Layouts/VortexLayout.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
61
using FluentUI.Blazor.Community.Helpers;
72
using Microsoft.AspNetCore.Components;
83

0 commit comments

Comments
 (0)