Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ obj
/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.jfm
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.dbmdl
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.jfm
/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.Development.json
53 changes: 53 additions & 0 deletions AdminUI/LearningHub.Nhs.AdminUI/Configuration/MediaKindSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace LearningHub.Nhs.AdminUI.Configuration
{
/// <summary>
/// Config AzureMediaSettings.
/// </summary>
public class MediaKindSettings
{
/// <summary>
/// Gets or sets subscription name.
/// </summary>
public string SubscriptionName { get; set; }

/// <summary>
/// Gets or sets token.
/// </summary>
public string Token { get; set; }

/// <summary>
/// Gets or sets storage media account name.
/// </summary>
public string StorageAccountName { get; set; }

/// <summary>
/// Gets or sets media kind media service issuer.
/// </summary>
public string Issuer { get; set; }

/// <summary>
/// Gets or sets media kind media service audience.
/// </summary>
public string Audience { get; set; }

/// <summary>
/// Gets or sets the contentkey policyname.
/// </summary>
public string ContentKeyPolicyName { get; set; }

/// <summary>
/// Gets or sets media kind media service jwt primary key secret.
/// </summary>
public string JWTPrimaryKeySecret { get; set; }

/// <summary>
/// Gets or sets the media kind media kind MKPlayer licence key.
/// </summary>
public string MKPlayerLicence { get; set; }

/// <summary>
/// Gets or sets the media kind blob connection string.
/// </summary>
public string MediaKindStorageConnectionString { get; set; }
}
}
5 changes: 5 additions & 0 deletions AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,10 @@ public class WebSettings
/// Gets or sets the FileUploadSettings.
/// </summary>
public FileUploadSettingsModel FileUploadSettings { get; set; } = new FileUploadSettingsModel();

/// <summary>
/// Gets or sets the MediaKindSettings.
/// </summary>
public MediaKindSettings MediaKindSettings { get; set; } = new MediaKindSettings();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,14 @@ public IActionResult GetAVUnavailableView()
[HttpGet("GetDisplayAVFlag")]
public bool GetDisplayAVFlag() => this.featureManager.IsEnabledAsync(FeatureFlags.DisplayAudioVideo).Result;

/// <summary>
/// The GetMKPlayerKey.
/// </summary>
/// <returns>Mediakind MK Player Key.</returns>
[Route("Resource/GetMKPlayerKey")]
[HttpGet("GetMKPlayerKey")]
public string GetMKPlayerKey() => this.websettings.Value.MediaKindSettings.MKPlayerLicence;

private static List<PagingOptionPair> FilterOptions()
{
List<PagingOptionPair> options = new List<PagingOptionPair>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="MK.IO" Version="1.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.14.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.14.1" />
Expand Down
163 changes: 124 additions & 39 deletions AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/content/cmsPageRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<h4 v-if="pageSectionDetail.sectionTitleElement=='h4'">{{pageSectionDetail.sectionTitle}}</h4>
</div>
</div>

<div :class="[`nhsuk-grid-row ${section.topMargin && (!pageSectionDetail || !pageSectionDetail.sectionTitle) ? 'information-page__row--padding-top' : '' } ${section.bottomMargin ? 'information-page__row--padding-bottom' : '' }`]">

<div v-if="sectionTemplateType === SectionTemplateType.Video" class="nhsuk-grid-column-two-thirds">
Expand All @@ -19,13 +18,15 @@
</div>
<div class="information-page__asset-container">
<div id="mediaContainer" :class="[`${disableVideoControl ? 'videoControlDisabled' : ''}`]" v-show="sectionTemplateType === SectionTemplateType.Video && displayAVFlag" class="w-100">
<video controls v-show="section.id" :id="[`azureMediaPlayer${section.id}`]"
<!--<video controls v-show="section.id" :id="[`azureMediaPlayer${section.id}`]"
data-setup='{"logo": { "enabled": false }, "techOrder": ["azureHtml5JS", "flashSS", "silverlightSS", "html5"], "nativeControlsForTouch": false, "fluid": true}'
class="azuremediaplayer amp-default-skin amp-big-play-centered" style="height:250px;">
<p class="amp-no-js">
To view this media please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
</p>
</video>
</video>-->
<div class="video-container" :id="getPlayerUniqueId"></div>

<div class="information-page__asset-link-container" v-if="pageSectionDetail && pageSectionDetail.videoAsset && pageSectionDetail.videoAsset.transcriptFile" :style="getTextBackbroundStyle">
<a download :style="getLinkStyle" :href="[`/file/download/${pageSectionDetail.videoAsset.transcriptFile.filePath}/${pageSectionDetail.videoAsset.transcriptFile.fileName}`]">
Download transcript
Expand Down Expand Up @@ -68,6 +69,7 @@
import { PageSectionDetailModel, SectionLayoutType, } from '../models/content/pageSectionDetailModel';
import { contentData } from '../data/content';
import { AzureMediaAssetModel } from '../models/content/videoAssetModel';
import { MKPlayer } from '@mediakind/mkplayer';

export default Vue.extend({
props: {
Expand All @@ -82,10 +84,14 @@
pageSectionDetail: null as PageSectionDetailModel,
disableVideoControl: false,
displayAVFlag: false,
audioVideoUnavailableView : '' as string,
audioVideoUnavailableView: '' as string,
player: null,
videoContainer: null,
mkioKey: '',
};
},
created() {
this.getMKIOPlayerKey();
this.load();
this.getDisplayAVFlag();
this.getAudioVideoUnavailableView();
Expand Down Expand Up @@ -145,19 +151,32 @@
},
isRightSectionLayout() {
return this.section.sectionLayoutType == SectionLayoutType.Right;
},
},
getPlayerUniqueId(): string {
return `videoContainer_${this.section.id}`
},
},
methods: {
getDisplayAVFlag() {
contentData.getDisplayAVFlag().then(response => {
this.displayAVFlag = response;
});
this.displayAVFlag = response;
});
},
getAudioVideoUnavailableView() {
contentData.getAVUnavailableView().then(response => {
this.audioVideoUnavailableView = response;
this.audioVideoUnavailableView = response;
});
},
onPlayerReady() {
const videoElement = document.getElementById("bitmovinplayer-video-" + this.getPlayerUniqueId) as HTMLVideoElement;
if (videoElement) {
videoElement.controls = true;
}
},
async getMKIOPlayerKey() {
this.mkioKey = await contentData.getMKPlayerKey();
//return this.mkioKey;
},
load() {
if (this.sectionTemplateType === SectionTemplateType.Video) {
contentData.getPageSectionDetailVideo(this.section.id).then(response => {
Expand All @@ -166,56 +185,101 @@
if (!this.pageSectionDetail.videoAsset)
return;

const id = 'azureMediaPlayer' + this.pageSectionDetail.id;
let azureMediaPlayer = amp(id);

if (this.pageSectionDetail.videoAsset.azureMediaAsset) {
$(`#${id}`).css({ 'height': '', 'border': '1px solid #768692' });
this.disableVideoControl = false;
} else {
this.disableVideoControl = true;
}

if (this.pageSectionDetail.videoAsset.thumbnailImageFile) {
azureMediaPlayer.poster(`/file/download/${this.pageSectionDetail.videoAsset.thumbnailImageFile.filePath}/${this.pageSectionDetail.videoAsset.thumbnailImageFile.fileName}`);
}
if (this.pageSectionDetail.videoAsset.azureMediaAsset && this.pageSectionDetail.videoAsset.closedCaptionsFile) {
azureMediaPlayer.src([{
type: "application/vnd.ms-sstr+xml",
src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
}],
[{ kind: "captions", src: `/file/download/${this.pageSectionDetail.videoAsset.closedCaptionsFile.filePath}/${this.pageSectionDetail.videoAsset.closedCaptionsFile.fileName}`, srclang: "en", label: "english" }]);
}
else if (this.pageSectionDetail.videoAsset.azureMediaAsset && !this.pageSectionDetail.videoAsset.closedCaptionsFile) {
azureMediaPlayer.src([{
type: "application/vnd.ms-sstr+xml",
src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
}]);
}
// Grab the video container
this.videoContainer = document.getElementById(this.getPlayerUniqueId);

var licenceKey = this.mkioKey;// this.getMKIOPlayerKey();

// Prepare the player configuration
const playerConfig = {
key: licenceKey,
ui: false,
theme: "dark",
events: {
ready: this.onPlayerReady,
}
};

// Initialize the player with video container and player configuration
this.player = new MKPlayer(this.videoContainer, playerConfig);

// Load source
const sourceConfig = {
hls: this.getMediaPlayUrl(this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri),
drm: {
clearkey: {
LA_URL: "HLS_AES",
headers: {
"Authorization": this.getBearerToken(this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken)
}
}
}
};

this.player.load(sourceConfig)
.then(() => {
console.log("Source loaded successfully!");
})
.catch(() => {
console.error("An error occurred while loading the source!");
});

//const id = 'azureMediaPlayer' + this.pageSectionDetail.id;
//let azureMediaPlayer = amp(id);

//if (this.pageSectionDetail.videoAsset.azureMediaAsset) {
// $(`#${id}`).css({ 'height': '', 'border': '1px solid #768692' });
// this.disableVideoControl = false;
//} else {
// this.disableVideoControl = true;
//}

//if (this.pageSectionDetail.videoAsset.thumbnailImageFile) {
// azureMediaPlayer.poster(`/file/download/${this.pageSectionDetail.videoAsset.thumbnailImageFile.filePath}/${this.pageSectionDetail.videoAsset.thumbnailImageFile.fileName}`);
//}
//if (this.pageSectionDetail.videoAsset.azureMediaAsset && this.pageSectionDetail.videoAsset.closedCaptionsFile) {
// azureMediaPlayer.src([{
// type: "application/vnd.ms-sstr+xml",
// src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
// protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
// }],
// [{ kind: "captions", src: `/file/download/${this.pageSectionDetail.videoAsset.closedCaptionsFile.filePath}/${this.pageSectionDetail.videoAsset.closedCaptionsFile.fileName}`, srclang: "en", label: "english" }]);
//}
//else if (this.pageSectionDetail.videoAsset.azureMediaAsset && !this.pageSectionDetail.videoAsset.closedCaptionsFile) {
// azureMediaPlayer.src([{
// type: "application/vnd.ms-sstr+xml",
// src: this.pageSectionDetail.videoAsset.azureMediaAsset.locatorUri,
// protectionInfo: [{ type: 'AES', authenticationToken: `Bearer=${this.pageSectionDetail.videoAsset.azureMediaAsset.authenticationToken}` }]
// }]);
//}
});
} else {
contentData.getPageSectionDetail(this.section.id).then(x => this.pageSectionDetail = x);
}
},
},
getAESProtection(token: string): string {
var aesProtectionInfo = '{"protectionInfo": [{"type": "AES", "authenticationToken":"Bearer=' + token + '"}], "streamingFormats":["SMOOTH","DASH"]}';
return aesProtectionInfo;
},
getMediaAssetProxyUrl(azureMediaAsset: AzureMediaAssetModel): string {

let playBackUrl = azureMediaAsset.locatorUri;
playBackUrl = playBackUrl.substring(0, playBackUrl.lastIndexOf("manifest")) + "manifest(format=m3u8-aapl)";

let sourceUrl = "/Media/MediaManifest?playBackUrl=" + playBackUrl + "&token=" + azureMediaAsset.authenticationToken;

return sourceUrl;
},
getBearerToken(token: string): string {
return "Bearer=" + token;
},
getMediaPlayUrl(url: string): string {
let sourceUrl = url.substring(0, url.lastIndexOf("manifest")) + "manifest(format=m3u8-cmaf,encryption=cbc)";
return sourceUrl;
},
},
watch: {
section() {
this.load();
this.load();
}
}
})
Expand All @@ -225,4 +289,25 @@
pointer-events: none;
opacity: 0.5;
}

.video-container {
height: 0;
width: 100%;
overflow: hidden;
position: relative;
padding-top: 56.25%; /* 16:9 aspect ratio */
background-color: #000;
}

video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

video[id^="bitmovinplayer-video"] {
width: 100%;
}
</style>
14 changes: 13 additions & 1 deletion AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/data/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ const getAVUnavailableView = async function (): Promise<string> {
throw e;
});
};
const getMKPlayerKey = async function (): Promise<string> {
return await axios.get('/Resource/GetMKPlayerKey')
.then(response => {
return response.data;
})
.catch(e => {
console.error('Error fetching Media Kind MKPlayer Key', e)
throw e;
});
};


export const contentData = {
getUploadSettings,
Expand All @@ -295,5 +306,6 @@ export const contentData = {
updateVideoAsset,
getAddAVFlag,
getDisplayAVFlag,
getAVUnavailableView
getAVUnavailableView,
getMKPlayerKey
};
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
services.AddScoped<ILogService, LogService>();
services.AddScoped<IRoadmapService, RoadmapService>();
services.AddScoped<IFileService, FileService>();
services.AddScoped<IAzureMediaService, AzureMediaService>();
services.AddScoped<IAzureMediaService, MKIOMediaService>();
services.AddScoped<IResourceSyncService, ResourceSyncService>();
services.AddScoped<ICatalogueService, CatalogueService>();
services.AddScoped<IContentService, ContentService>();
Expand Down
Loading