Skip to content

Commit 7725ee0

Browse files
committed
conflicts resolved
2 parents 0ed131f + 0fc75cc commit 7725ee0

40 files changed

+4001
-34708
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ obj
4747
/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.jfm
4848
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.dbmdl
4949
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.jfm
50+
/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.Development.json
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace LearningHub.Nhs.AdminUI.Configuration
2+
{
3+
/// <summary>
4+
/// Config AzureMediaSettings.
5+
/// </summary>
6+
public class MediaKindSettings
7+
{
8+
/// <summary>
9+
/// Gets or sets subscription name.
10+
/// </summary>
11+
public string SubscriptionName { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets token.
15+
/// </summary>
16+
public string Token { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets storage media account name.
20+
/// </summary>
21+
public string StorageAccountName { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets media kind media service issuer.
25+
/// </summary>
26+
public string Issuer { get; set; }
27+
28+
/// <summary>
29+
/// Gets or sets media kind media service audience.
30+
/// </summary>
31+
public string Audience { get; set; }
32+
33+
/// <summary>
34+
/// Gets or sets the contentkey policyname.
35+
/// </summary>
36+
public string ContentKeyPolicyName { get; set; }
37+
38+
/// <summary>
39+
/// Gets or sets media kind media service jwt primary key secret.
40+
/// </summary>
41+
public string JWTPrimaryKeySecret { get; set; }
42+
43+
/// <summary>
44+
/// Gets or sets the media kind media kind MKPlayer licence key.
45+
/// </summary>
46+
public string MKPlayerLicence { get; set; }
47+
48+
/// <summary>
49+
/// Gets or sets the media kind blob connection string.
50+
/// </summary>
51+
public string MediaKindStorageConnectionString { get; set; }
52+
}
53+
}

AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,10 @@ public class WebSettings
156156
/// Gets or sets the FileUploadSettings.
157157
/// </summary>
158158
public FileUploadSettingsModel FileUploadSettings { get; set; } = new FileUploadSettingsModel();
159+
160+
/// <summary>
161+
/// Gets or sets the MediaKindSettings.
162+
/// </summary>
163+
public MediaKindSettings MediaKindSettings { get; set; } = new MediaKindSettings();
159164
}
160165
}

AdminUI/LearningHub.Nhs.AdminUI/Controllers/ResourceController.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,14 @@ public IActionResult GetAVUnavailableView()
360360
[HttpGet("GetDisplayAVFlag")]
361361
public bool GetDisplayAVFlag() => this.featureManager.IsEnabledAsync(FeatureFlags.DisplayAudioVideo).Result;
362362

363+
/// <summary>
364+
/// The GetMKPlayerKey.
365+
/// </summary>
366+
/// <returns>Mediakind MK Player Key.</returns>
367+
[Route("Resource/GetMKPlayerKey")]
368+
[HttpGet("GetMKPlayerKey")]
369+
public string GetMKPlayerKey() => this.websettings.Value.MediaKindSettings.MKPlayerLicence;
370+
363371
private static List<PagingOptionPair> FilterOptions()
364372
{
365373
List<PagingOptionPair> options = new List<PagingOptionPair>();

AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@
105105
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
106106
</PackageReference>
107107
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.0" />
108-
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
108+
<PackageReference Include="MK.IO" Version="1.6.0" />
109+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
109110
<PackageReference Include="NLog.Web.AspNetCore" Version="4.14.0" />
110111
<PackageReference Include="System.Data.SqlClient" Version="4.8.3" />
111112
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.14.1" />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export enum MKPlayerType {
2+
Html5 = "html5",
3+
Native = "native",
4+
WebRtc = "webrtc",
5+
Unknown = "unknown"
6+
}
7+
export enum MKStreamType {
8+
Hls = "hls",
9+
Dash = "dash",
10+
Progressive = "progressive",
11+
Smooth = "smooth",
12+
Whep = "whep",
13+
Unknown = "unknown"
14+
};

AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/content/cmsPageRow.vue

Lines changed: 130 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
<h4 v-if="pageSectionDetail.sectionTitleElement=='h4'">{{pageSectionDetail.sectionTitle}}</h4>
1111
</div>
1212
</div>
13-
1413
<div :class="[`nhsuk-grid-row ${section.topMargin && (!pageSectionDetail || !pageSectionDetail.sectionTitle) ? 'information-page__row--padding-top' : '' } ${section.bottomMargin ? 'information-page__row--padding-bottom' : '' }`]">
1514

1615
<div v-if="sectionTemplateType === SectionTemplateType.Video" class="nhsuk-grid-column-two-thirds">
@@ -19,13 +18,15 @@
1918
</div>
2019
<div class="information-page__asset-container">
2120
<div id="mediaContainer" :class="[`${disableVideoControl ? 'videoControlDisabled' : ''}`]" v-show="sectionTemplateType === SectionTemplateType.Video && displayAVFlag" class="w-100">
22-
<video controls v-show="section.id" :id="[`azureMediaPlayer${section.id}`]"
21+
<!--<video controls v-show="section.id" :id="[`azureMediaPlayer${section.id}`]"
2322
data-setup='{"logo": { "enabled": false }, "techOrder": ["azureHtml5JS", "flashSS", "silverlightSS", "html5"], "nativeControlsForTouch": false, "fluid": true}'
2423
class="azuremediaplayer amp-default-skin amp-big-play-centered" style="height:250px;">
2524
<p class="amp-no-js">
2625
To view this media please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
2726
</p>
28-
</video>
27+
</video>-->
28+
<div class="video-container" :id="getPlayerUniqueId"></div>
29+
2930
<div class="information-page__asset-link-container" v-if="pageSectionDetail && pageSectionDetail.videoAsset && pageSectionDetail.videoAsset.transcriptFile" :style="getTextBackbroundStyle">
3031
<a download :style="getLinkStyle" :href="[`/file/download/${pageSectionDetail.videoAsset.transcriptFile.filePath}/${pageSectionDetail.videoAsset.transcriptFile.fileName}`]">
3132
Download transcript
@@ -68,6 +69,8 @@
6869
import { PageSectionDetailModel, SectionLayoutType, } from '../models/content/pageSectionDetailModel';
6970
import { contentData } from '../data/content';
7071
import { AzureMediaAssetModel } from '../models/content/videoAssetModel';
72+
import { MKPlayer } from '@mediakind/mkplayer';
73+
import { MKPlayerType, MKStreamType } from '../MKPlayerConfigEnum';
7174
7275
export default Vue.extend({
7376
props: {
@@ -82,10 +85,14 @@
8285
pageSectionDetail: null as PageSectionDetailModel,
8386
disableVideoControl: false,
8487
displayAVFlag: false,
85-
audioVideoUnavailableView : '' as string,
88+
audioVideoUnavailableView: '' as string,
89+
player: null,
90+
videoContainer: null,
91+
mkioKey: '',
8692
};
8793
},
88-
created() {
94+
async created(): Promise<void> {
95+
await this.getMKIOPlayerKey();
8996
this.load();
9097
this.getDisplayAVFlag();
9198
this.getAudioVideoUnavailableView();
@@ -145,19 +152,32 @@
145152
},
146153
isRightSectionLayout() {
147154
return this.section.sectionLayoutType == SectionLayoutType.Right;
148-
},
155+
},
156+
getPlayerUniqueId(): string {
157+
return `videoContainer_${this.section.id}`
158+
},
149159
},
150160
methods: {
151161
getDisplayAVFlag() {
152162
contentData.getDisplayAVFlag().then(response => {
153-
this.displayAVFlag = response;
154-
});
163+
this.displayAVFlag = response;
164+
});
155165
},
156166
getAudioVideoUnavailableView() {
157167
contentData.getAVUnavailableView().then(response => {
158-
this.audioVideoUnavailableView = response;
168+
this.audioVideoUnavailableView = response;
159169
});
160170
},
171+
onPlayerReady() {
172+
const videoElement = document.getElementById("bitmovinplayer-video-" + this.getPlayerUniqueId) as HTMLVideoElement;
173+
if (videoElement) {
174+
videoElement.controls = true;
175+
}
176+
},
177+
async getMKIOPlayerKey(): Promise<void> {
178+
this.mkioKey = await contentData.getMKPlayerKey();
179+
//return this.mkioKey;
180+
},
161181
load() {
162182
if (this.sectionTemplateType === SectionTemplateType.Video) {
163183
contentData.getPageSectionDetailVideo(this.section.id).then(response => {
@@ -166,56 +186,108 @@
166186
if (!this.pageSectionDetail.videoAsset)
167187
return;
168188
169-
const id = 'azureMediaPlayer' + this.pageSectionDetail.id;
170-
let azureMediaPlayer = amp(id);
189+
// Grab the video container
190+
this.videoContainer = document.getElementById(this.getPlayerUniqueId);
171191
172-
if (this.pageSectionDetail.videoAsset.azureMediaAsset) {
173-
$(`#${id}`).css({ 'height': '', 'border': '1px solid #768692' });
174-
this.disableVideoControl = false;
175-
} else {
176-
this.disableVideoControl = true;
192+
if(!this.mkioKey) {
193+
this.getMKIOPlayerKey();
177194
}
178195
179-
if (this.pageSectionDetail.videoAsset.thumbnailImageFile) {
180-
azureMediaPlayer.poster(`/file/download/${this.pageSectionDetail.videoAsset.thumbnailImageFile.filePath}/${this.pageSectionDetail.videoAsset.thumbnailImageFile.fileName}`);
181-
}
182-
if (this.pageSectionDetail.videoAsset.azureMediaAsset && this.pageSectionDetail.videoAsset.closedCaptionsFile) {
183-
azureMediaPlayer.src([{
184-
type: "application/vnd.ms-sstr+xml",
185-
src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
186-
protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
187-
}],
188-
[{ kind: "captions", src: `/file/download/${this.pageSectionDetail.videoAsset.closedCaptionsFile.filePath}/${this.pageSectionDetail.videoAsset.closedCaptionsFile.fileName}`, srclang: "en", label: "english" }]);
189-
}
190-
else if (this.pageSectionDetail.videoAsset.azureMediaAsset && !this.pageSectionDetail.videoAsset.closedCaptionsFile) {
191-
azureMediaPlayer.src([{
192-
type: "application/vnd.ms-sstr+xml",
193-
src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
194-
protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
195-
}]);
196-
}
196+
// Prepare the player configuration
197+
const playerConfig = {
198+
key: this.mkioKey,
199+
ui: false,
200+
playback: {
201+
muted: false,
202+
autoplay: false,
203+
preferredTech: [{ player: MKPlayerType.Html5, streaming: MKStreamType.Hls }] // to force the player to use html5 player instead of native on safari
204+
},
205+
theme: "dark",
206+
events: {
207+
ready: this.onPlayerReady,
208+
}
209+
};
210+
211+
// Initialize the player with video container and player configuration
212+
this.player = new MKPlayer(this.videoContainer, playerConfig);
213+
214+
// Load source
215+
const sourceConfig = {
216+
hls: this.getMediaPlayUrl(this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri),
217+
drm: {
218+
clearkey: {
219+
LA_URL: "HLS_AES",
220+
headers: {
221+
"Authorization": this.getBearerToken(this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken)
222+
}
223+
}
224+
}
225+
};
226+
227+
this.player.load(sourceConfig)
228+
.then(() => {
229+
console.log("Source loaded successfully!");
230+
})
231+
.catch(() => {
232+
console.error("An error occurred while loading the source!");
233+
});
234+
235+
//const id = 'azureMediaPlayer' + this.pageSectionDetail.id;
236+
//let azureMediaPlayer = amp(id);
237+
238+
//if (this.pageSectionDetail.videoAsset.azureMediaAsset) {
239+
// $(`#${id}`).css({ 'height': '', 'border': '1px solid #768692' });
240+
// this.disableVideoControl = false;
241+
//} else {
242+
// this.disableVideoControl = true;
243+
//}
244+
245+
//if (this.pageSectionDetail.videoAsset.thumbnailImageFile) {
246+
// azureMediaPlayer.poster(`/file/download/${this.pageSectionDetail.videoAsset.thumbnailImageFile.filePath}/${this.pageSectionDetail.videoAsset.thumbnailImageFile.fileName}`);
247+
//}
248+
//if (this.pageSectionDetail.videoAsset.azureMediaAsset && this.pageSectionDetail.videoAsset.closedCaptionsFile) {
249+
// azureMediaPlayer.src([{
250+
// type: "application/vnd.ms-sstr+xml",
251+
// src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
252+
// protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
253+
// }],
254+
// [{ kind: "captions", src: `/file/download/${this.pageSectionDetail.videoAsset.closedCaptionsFile.filePath}/${this.pageSectionDetail.videoAsset.closedCaptionsFile.fileName}`, srclang: "en", label: "english" }]);
255+
//}
256+
//else if (this.pageSectionDetail.videoAsset.azureMediaAsset && !this.pageSectionDetail.videoAsset.closedCaptionsFile) {
257+
// azureMediaPlayer.src([{
258+
// type: "application/vnd.ms-sstr+xml",
259+
// src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
260+
// protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
261+
// }]);
262+
//}
197263
});
198264
} else {
199265
contentData.getPageSectionDetail(this.section.id).then(x => this.pageSectionDetail = x);
200266
}
201-
},
267+
},
202268
getAESProtection(token: string): string {
203269
var aesProtectionInfo = '{"protectionInfo": [{"type": "AES", "authenticationToken":"Bearer=' + token + '"}], "streamingFormats":["SMOOTH","DASH"]}';
204270
return aesProtectionInfo;
205271
},
206272
getMediaAssetProxyUrl(azureMediaAsset: AzureMediaAssetModel): string {
207-
208273
let playBackUrl = azureMediaAsset.locatorUri;
209274
playBackUrl = playBackUrl.substring(0, playBackUrl.lastIndexOf("manifest")) + "manifest(format=m3u8-aapl)";
210275
211276
let sourceUrl = "/Media/MediaManifest?playBackUrl=" + playBackUrl + "&token=" + azureMediaAsset.authenticationToken;
212277
213278
return sourceUrl;
214279
},
280+
getBearerToken(token: string): string {
281+
return "Bearer=" + token;
282+
},
283+
getMediaPlayUrl(url: string): string {
284+
let sourceUrl = url.substring(0, url.lastIndexOf("manifest")) + "manifest(format=m3u8-cmaf,encryption=cbc)";
285+
return sourceUrl;
286+
},
215287
},
216288
watch: {
217289
section() {
218-
this.load();
290+
this.load();
219291
}
220292
}
221293
})
@@ -225,4 +297,25 @@
225297
pointer-events: none;
226298
opacity: 0.5;
227299
}
300+
301+
.video-container {
302+
height: 0;
303+
width: 100%;
304+
overflow: hidden;
305+
position: relative;
306+
padding-top: 56.25%; /* 16:9 aspect ratio */
307+
background-color: #000;
308+
}
309+
310+
video {
311+
position: absolute;
312+
top: 0;
313+
left: 0;
314+
width: 100%;
315+
height: 100%;
316+
}
317+
318+
video[id^="bitmovinplayer-video"] {
319+
width: 100%;
320+
}
228321
</style>

AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/content/upload/fileValidation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const file_size_validation = (value: any) => {
88
export const file_extension_validation = (value: any) => {
99
if (!value) { return true; }
1010
let fileExtension = value.name.split(".").pop();
11-
let fileType = ['mp4', 'avi', 'm4v', 'mov', 'mkv', 'mpg', 'mpeg', 'wmv'].find(ext => ext == fileExtension);
11+
let fileType = ['mp4', 'avi', 'm4v', 'mov', 'mkv', 'mpg', 'm2v', 'vob'].find(ext => ext == fileExtension);
1212
return fileType != undefined;
1313
};
1414
export const transcriptfile_extension_validation = (value: any) => {

AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/data/content.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,17 @@ const getAVUnavailableView = async function (): Promise<string> {
273273
throw e;
274274
});
275275
};
276+
const getMKPlayerKey = async function (): Promise<string> {
277+
return await axios.get('/Resource/GetMKPlayerKey')
278+
.then(response => {
279+
return response.data;
280+
})
281+
.catch(e => {
282+
console.error('Error fetching Media Kind MKPlayer Key', e)
283+
throw e;
284+
});
285+
};
286+
276287

277288
export const contentData = {
278289
getUploadSettings,
@@ -295,5 +306,6 @@ export const contentData = {
295306
updateVideoAsset,
296307
getAddAVFlag,
297308
getDisplayAVFlag,
298-
getAVUnavailableView
309+
getAVUnavailableView,
310+
getMKPlayerKey
299311
};

0 commit comments

Comments
 (0)