Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
668804b
MediaKind settings added
binon May 2, 2024
738162c
Updates appsettins fo rMKIO
binon May 3, 2024
beee082
Added MKIO player dummy vue page
binon May 3, 2024
1822a04
Merge pull request #357 from TechnologyEnhancedLearning/RC
binon May 15, 2024
5c7b915
MKIO changes before refactoring
binon May 20, 2024
393413c
Fix for the build issue
binon May 21, 2024
122c921
tidy up adminUI page with media kind player
binon May 21, 2024
909932b
refactored the landing page with MK Player
binon May 21, 2024
ba08fc4
Working code commit
binon May 23, 2024
2e155fa
Updated audio settings in resource view
binon May 23, 2024
01fce55
Updated video tracking for paused and stopped events
binon May 24, 2024
e9af4cb
Fixed the timeout issue when uploading video
binon May 28, 2024
f62f14a
Minor changeon video tracking
binon May 28, 2024
d024db5
Update the way the mediakind storage access the blobs
binon May 31, 2024
bae6136
Updated the licencekey retrival
binon Jun 3, 2024
8f3a7fb
Merge pull request #390 from TechnologyEnhancedLearning/RC
binon Jun 3, 2024
0485b85
Fixing the media time issue and cloning issue.
binon Jun 5, 2024
8f1199e
Merge pull request #400 from TechnologyEnhancedLearning/RC
AnjuJose011 Jun 7, 2024
1351b34
Added post deplyment scripts
binon Jun 7, 2024
3565b31
Merge pull request #402 from TechnologyEnhancedLearning/RC
binon Jun 7, 2024
8e79ee0
Merge pull request #403 from TechnologyEnhancedLearning/Develop/Featu…
binon Jun 7, 2024
c7bb7f4
Making consistent layout on mediakind player and update sql script file
binon Jun 10, 2024
add096f
Merge pull request #409 from TechnologyEnhancedLearning/Develop/Featu…
binon Jun 10, 2024
44d4599
Fixed the error while launching the scorm content and javascript error
binon Jun 10, 2024
368f67b
Merge pull request #410 from TechnologyEnhancedLearning/Develop/Featu…
AnjuJose011 Jun 10, 2024
df24f35
Merge pull request #418 from TechnologyEnhancedLearning/RC
AnjuJose011 Jun 12, 2024
5fe0beb
Merge pull request #421 from TechnologyEnhancedLearning/RC
binon Jun 14, 2024
f4537e5
video tracking issue has been resolved and update the validaiton on A…
binon Jun 14, 2024
9e2de89
Merge pull request #422 from TechnologyEnhancedLearning/Develop/Featu…
binon Jun 14, 2024
5069859
Merge pull request #425 from TechnologyEnhancedLearning/CI
AnjuJose011 Jun 17, 2024
4a415d9
conflicts resolved
AnjuJose011 Jun 19, 2024
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
165 changes: 128 additions & 37 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() {
async created(): Promise<void> {
await 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(): Promise<void> {
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,107 @@
if (!this.pageSectionDetail.videoAsset)
return;

const id = 'azureMediaPlayer' + this.pageSectionDetail.id;
let azureMediaPlayer = amp(id);
// Grab the video container
this.videoContainer = document.getElementById(this.getPlayerUniqueId);

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

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}` }]
}]);
}
// Prepare the player configuration
const playerConfig = {
key: this.mkioKey,
ui: false,
playback: {
muted: false,
autoplay: 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 +295,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>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const file_size_validation = (value: any) => {
export const file_extension_validation = (value: any) => {
if (!value) { return true; }
let fileExtension = value.name.split(".").pop();
let fileType = ['mp4', 'avi', 'm4v', 'mov', 'mkv', 'mpg', 'mpeg', 'wmv'].find(ext => ext == fileExtension);
let fileType = ['mp4', 'avi', 'm4v', 'mov', 'mkv', 'mpg', 'm2v', 'vob'].find(ext => ext == fileExtension);
return fileType != undefined;
};
export const transcriptfile_extension_validation = (value: any) => {
Expand Down
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