initial commit (#303)

This commit is contained in:
Joseph Velliah 2017-09-08 00:53:36 -05:00 committed by Vesa Juvonen
parent e446a84ec4
commit f82957cc56
28 changed files with 981 additions and 0 deletions

View File

@ -0,0 +1,64 @@
# Media Recorder
## Summary
Sample SharePoint Framework client-side web part illustrating Video Recording using [MediaRecorder Web API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder).
This is an experimental web part. Because this technology's specification has not stabilized, check the compatibility table for usage in various browsers. Also note that the syntax and behavior of an experimental technology is subject to change in future versions of browsers as the specification changes
## Browser with MediaRecorder API support
![Sample SharePoint Framework client-side web part illustrating Video Recording using MediaRecorder Web API](./assets/browsersupported.gif)
## Browser without MediaRecorder API support
![Sample SharePoint Framework client-side web part illustrating Video Recording using MediaRecorder Web API](./assets/browserunsupported.gif)
## Applies to
* [SharePoint Framework Developer](https://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Solution
Solution|Author(s)
--------|---------
angular-media-recorder|Joseph Velliah (SPRIDER, @sprider)
## Version history
Version|Date|Comments
-------|----|--------
1.0|September 04, 2017|Initial release
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Minimal Path to Awesome
- clone this repo
- go to the working directory of the webpart folder in the command line run:
- npm and typings install
- npm install @types/microsoft-ajax --save-dev
- npm install @types/sharepoint --save-dev
- npm install @types/angular --save-dev
- gulp serve
- Open the workbench page in a sharepoint site (https://{yoursiteurl}/_layouts/15/workbench.aspx)
- add Media Recorder webpart and edit it
- configure the library name where you would like to save the recording/input file.
- start recording
- allow camera and mic
- stop recording
- replay video
- retry or upload the recorded video
## Features
This project illustrates the following concepts:
- Front/Back camera selection
- Video recording using the supported browsers
- File uploading option for the unsupported browsers
- Playback recording
- Upload files to document library

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

View File

@ -0,0 +1,13 @@
{
"entries": [
{
"entry": "./lib/webparts/mediaRecorder/MediaRecorderWebPart.js",
"manifest": "./src/webparts/mediaRecorder/MediaRecorderWebPart.manifest.json",
"outputPath": "./dist/media-recorder.bundle.js"
}
],
"externals": {},
"localizedResources": {
"mediaRecorderStrings": "webparts/mediaRecorder/loc/{locale}.js"
}
}

View File

@ -0,0 +1,3 @@
{
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,6 @@
{
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "angular-media-recorder",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,10 @@
{
"solution": {
"name": "angular-media-recorder",
"id": "ecaddd68-1036-43fb-bdbf-3cce7f764328",
"version": "1.0.0.0"
},
"paths": {
"zippedPackage": "solution/angular-media-recorder.sppkg"
}
}

View File

@ -0,0 +1,9 @@
{
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,45 @@
{
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-case": true,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-unused-imports": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false
}
}
}

View File

@ -0,0 +1,3 @@
{
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,6 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.initialize(gulp);

View File

@ -0,0 +1,30 @@
{
"name": "angular-media-recorder",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-core-library": "~1.1.0",
"@microsoft/sp-webpart-base": "~1.1.1",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"angular": "^1.6.6"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.1.0",
"@microsoft/sp-module-interfaces": "~1.1.0",
"@microsoft/sp-webpart-workbench": "~1.1.0",
"@types/angular": "^1.6.32",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/microsoft-ajax": "0.0.33",
"@types/mocha": ">=2.2.33 <2.6.0",
"@types/sharepoint": "^2013.1.7",
"gulp": "~3.9.1"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
}
}

View File

@ -0,0 +1,3 @@
export interface IMediaRecorderWebPartProps {
listName: string;
}

View File

@ -0,0 +1,43 @@
.mediaRecorder {
.container {
max-width: 700px;
background-color: #29ACFF;
color: white;
}
.mediaRecorderSection {
min-height: 100%;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
}
.mediaRecorderVideo {
width: 100%;
height: 100%;
}
.mediaRecorderButton {
display: block;
width: 25%;
margin-left: auto;
margin-right: auto;
height: 3.333em;
margin-top: 1.1em;
background-color: #333;
color: white;
border: none;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@ -0,0 +1,26 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "784959fa-e575-4b95-8c4c-cb6116f6aefa",
"alias": "MediaRecorderWebPart",
"componentType": "WebPart",
"version": "*", // The "*" signifies that the version should be taken from the package.json
"manifestVersion": 2,
/**
* This property should only be set to true if it is certain that the webpart does not
* allow arbitrary scripts to be called
*/
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [{
"groupId": "784959fa-e575-4b95-8c4c-cb6116f6aefa",
"group": { "default": "Under Development" },
"title": { "default": "Media Recorder" },
"description": { "default": "Sample SharePoint Framework client-side web part illustrating Video Recording using MediaRecorder Web API" },
"officeFabricIconFontName": "Video",
"properties": {
"listName": null
}
}]
}

View File

@ -0,0 +1,114 @@
import { Version } from '@microsoft/sp-core-library';
import {
IWebPartContext,
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'mediaRecorderStrings';
import { IMediaRecorderWebPartProps } from './IMediaRecorderWebPartProps';
import { SPComponentLoader } from '@microsoft/sp-loader';
import * as angular from 'angular';
import './app/app-module';
import Home from './app/Home';
export default class MediaRecorderWebPart extends BaseClientSideWebPart<IMediaRecorderWebPartProps> {
private $injector: ng.auto.IInjectorService;
public constructor(context: IWebPartContext) {
super();
}
public render(): void {
if (this.renderedOnce === false) {
this.domElement.innerHTML = Home.templateHtml;
this.componentDidMount();
}
this.sendWebPartProperties();
}
private componentDidMount(): void {
try {
if (!window["SP"]) {
SPComponentLoader.loadScript('/_layouts/15/init.js', {
globalExportsName: '$_global_init'
})
.then((): Promise<{}> => {
return SPComponentLoader.loadScript('/_layouts/15/MicrosoftAjax.js', {
globalExportsName: 'Sys'
});
})
.then((): Promise<{}> => {
return SPComponentLoader.loadScript('/_layouts/15/SP.Core.js', {
globalExportsName: 'SP'
});
})
.then((): Promise<{}> => {
return SPComponentLoader.loadScript('/_layouts/15/SP.Runtime.js', {
globalExportsName: 'SP'
});
})
.then((): Promise<{}> => {
return SPComponentLoader.loadScript('/_layouts/15/SP.js', {
globalExportsName: 'SP'
});
})
.then((): void => {
this.$injector = angular.bootstrap(this.domElement, ['mediarecorderapp']);
this.sendWebPartProperties();
});
}
else {
this.$injector = angular.bootstrap(this.domElement, ['mediarecorderapp']);
this.sendWebPartProperties();
}
}
catch (error) {
console.info("Error in componentDidMount():" + error);
}
}
private sendWebPartProperties(): void {
if (this.$injector) {
this.$injector.get('$rootScope').$broadcast('configurationChanged', {
listName: this.properties.listName,
httpClient: this.context.spHttpClient,
webUrl: this.context.pageContext.web.absoluteUrl
});
}
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
protected get disableReactivePropertyChanges(): boolean {
return true;
}
}

View File

@ -0,0 +1,47 @@
import { IFile } from "../app/IFile";
/*=================
| Custom Directive to read files from input control |
=================*/
export class CustomFileChange implements ng.IDirective {
constructor(private $parse: ng.IParseService) {
}
public restrict = "A";
public link = (scope: ng.IScope, element: any, attrs: any) => {
const model = this.$parse(attrs.customFileChange);
const modelSetter = model.assign;
element.bind("change", (): void => {
scope.$apply((): void => {
const reader = new FileReader();
reader.onload = (event: any): void => {
const fileModel: IFile = {
fileName: element[0].files[0].name,
fileAsBuffer: event.target.result
};
modelSetter(scope, fileModel);
};
reader.onerror = (event: any): void => {
console.error(event.target.error);
};
reader.readAsArrayBuffer(element[0].files[0]);
});
});
}
public static factory(): ng.IDirectiveFactory {
const directive = ($parse: ng.IParseService) => new CustomFileChange($parse);
directive.$inject = ['$parse'];
return directive;
}
}

View File

@ -0,0 +1,95 @@
/*=================
| Service methods |
=================*/
export interface IDataService {
uploadFile(arrayBuffer: any, fileName: string, url: any, listName: string): ng.IPromise<string>;
getFileBuffer(file: any): ng.IPromise<any>;
arrayBufferToBase64(buffer: any): any;
}
export default class DataService implements IDataService {
public static $inject: string[] = ['$q', '$http', '$log'];
constructor(private $q: ng.IQService, private $http: ng.IHttpService, private $log: ng.ILogService)
{ }
/*=================
| Upload file to SharePoint |
=================*/
public uploadFile(arrayBuffer: any, fileName: string, url: any, listName: string): ng.IPromise<string> {
const deferred: ng.IDeferred<string> = this.$q.defer();
try {
var clientContext = new SP.ClientContext(url);
var oWeb = clientContext.get_web();
var oList = oWeb.get_lists().getByTitle(listName);
var createInfo = new SP.FileCreationInformation();
createInfo.set_content(arrayBuffer);
createInfo.set_url(fileName);
var uploadedDocument = oList.get_rootFolder().get_files().add(createInfo);
clientContext.load(uploadedDocument, 'ListItemAllFields');
clientContext.executeQueryAsync((sender: any, args: SP.ClientRequestSucceededEventArgs): void => {
deferred.resolve(uploadedDocument.get_listItemAllFields().get_id().toString());
}, (sender: any, args: SP.ClientRequestFailedEventArgs): void => {
deferred.reject('Error Message - ' + args.get_message() + ' . Stack Trace - ' + args.get_stackTrace());
});
}
catch (err) {
deferred.reject(err);
}
return deferred.promise;
}
/*=================
| File Buffer for the given file |
=================*/
public getFileBuffer(file: any): ng.IPromise<any> {
const deferred: ng.IDeferred<any> = this.$q.defer();
try {
var reader = new FileReader();
reader.onloadend = function (e) {
var contents: any = e.target;
deferred.resolve(contents.result);
};
reader.onerror = function (e) {
var contents: any = e.target;
deferred.reject(contents.error);
};
reader.readAsArrayBuffer(file);
}
catch (err) {
deferred.reject(err);
}
return deferred.promise;
}
/*=================
| Convert array buffer to Base64 |
=================*/
public arrayBufferToBase64(buffer: any): any {
let binary = '';
try {
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return binary;
}
catch (err) {
return '';
}
}
}

View File

@ -0,0 +1,39 @@
import styles from '../MediaRecorder.module.scss';
export default class Home {
public static templateHtml: string = `
<div class="${styles.mediaRecorder}" ng-cloak>
<div class="${styles.container}" data-ng-controller="HomeController as hctrl">
<div ng-if="hctrl.message">
<div class="${styles.mediaRecorderSection}">
{{hctrl.message}}
</div>
</div>
<div ng-if="hctrl.configurationNeeded && !hctrl.isLoading">
<div class="${styles.mediaRecorderSection}">
Please configure this webpart
</div>
</div>
<div ng-if="!hctrl.configurationNeeded">
<div class="${styles.mediaRecorderSection}" ng-if="hctrl.isLoading">
Loading...
</div>
<div class="${styles.mediaRecorderSection}" ng-if="!hctrl.mediaRecorderApi">
<input value="hctrl.file.fileName" data-custom-file-change="hctrl.file" type="file" accept="video/*" capture="user"/>
<button class="${styles.mediaRecorderButton}" ng-model="hctrl.btnFileUpload" data-ng-click="hctrl.handleFileUpload($event)">Upload File</button>
</div>
<div class="${styles.mediaRecorderSection}" ng-if="hctrl.mediaRecorderApi">
<playback></playback>
<video class="${styles.mediaRecorderVideo}" ng-if="hctrl.showVideo" ng-model="hctrl.vdRecorder" autoPlay muted ></video>
<button class="${styles.mediaRecorderButton}" ng-if="hctrl.showCameraSelection" ng-model="hctrl.frontCamera" ng-click="hctrl.cameraChange($event)">Use Back Camera</button>
<button class="${styles.mediaRecorderButton}" ng-if="hctrl.showStart" ng-model="hctrl.btnStart" ng-click="hctrl.handleVideoRecording($event)">Start Recording</button>
<button class="${styles.mediaRecorderButton}" ng-if="hctrl.showStop" ng-model="hctrl.btnStop" ng-click="hctrl.handleVideoStop($event)">Stop Recording</button>
<button class="${styles.mediaRecorderButton}" ng-if="hctrl.showUpload" ng-model="hctrl.btnUpload" ng-click="hctrl.handleVideoUpload($event)">Upload Recording</button>
<button class="${styles.mediaRecorderButton}" ng-if="hctrl.showRetry" ng-model="hctrl.btnRetry" ng-click="hctrl.handleVideoRetry($event)">Retry</button>
</div>
</div>
</div>
</div>
`;
}

View File

@ -0,0 +1,354 @@
import { IDataService, } from "../app/DataService";
import { IFile } from "../app/IFile";
import styles from '../MediaRecorder.module.scss';
export default class HomeController {
/*=================
| WebPart Properties |
=================*/
private listName: string = null;
private webUrl: string = null;
/*=================
| Controller Objects |
=================*/
public file: IFile;
private fileBlob: any = null;
private currentStream: any = null;
private recordedBlobs: any = [];
private recordedBlobType: any = null;
private message: string = null;
private vdRecorderSrc: string = null;
private isLoading: boolean = true;
private frontCamera: boolean = true;
private mediaRecorderApi: boolean = false;
private configurationNeeded: boolean = true;
private showVideo: boolean = true;
private showStart: boolean = true;
private showStop: boolean = false;
private showUpload: boolean = false;
private showRetry: boolean = false;
private showCameraSelection: boolean = true;
public static $inject: string[] = ['DataService', '$window', '$rootScope', '$scope'];
constructor(private dataService: IDataService, private $window: ng.IWindowService, private $rootScope: ng.IRootScopeService, private $scope: ng.IScope) {
const hctrl: HomeController = this;
/*=================
| Property changed event in rootScope|
=================*/
$rootScope.$on('configurationChanged', (event: ng.IAngularEvent, args: { listName: string; webUrl: string }): void => {
hctrl.init(args.listName, args.webUrl);
});
}
/*=================
| View Initialization |
=================*/
private init(propListName: string, propWebUrl: string): void {
const hctrl: HomeController = this;
hctrl.isLoading = true;
hctrl.file = null;
hctrl.fileBlob = null;
hctrl.currentStream = null;
hctrl.recordedBlobs = [];
hctrl.recordedBlobType = null;
hctrl.message = null;
hctrl.vdRecorderSrc = null;
hctrl.frontCamera = true;
hctrl.mediaRecorderApi = false;
hctrl.configurationNeeded = true;
hctrl.showVideo = true;
hctrl.showStart = true;
hctrl.showStop = false;
hctrl.showUpload = false;
hctrl.showRetry = false;
hctrl.showCameraSelection = true;
if (propListName != null && propListName.length > 0 &&
propWebUrl != null && propWebUrl.length > 0) {
hctrl.listName = propListName;
hctrl.webUrl = propWebUrl;
hctrl.configurationNeeded = false;
}
else {
hctrl.configurationNeeded = true;
hctrl.isLoading = false;
setTimeout(function () {
hctrl.$scope.$apply();
}, 100);
return;
}
// show/hide relevant video upload buttons depending on browser capabilities
if ((window as any).MediaRecorder) {
hctrl.mediaRecorderApi = true;
hctrl.isLoading = false;
console.info('This browser does support the MediaRecorder API.');
} else {
hctrl.mediaRecorderApi = false;
hctrl.isLoading = false;
console.info('This browser does not support the MediaRecorder API.');
}
setTimeout(function () {
hctrl.$scope.$apply();
}, 100);
}
/*=================
| Start Video Recording |
=================*/
private handleVideoRecording(event?: any): void {
const hctrl: HomeController = this;
try {
// First get ahold of getUserMedia, if present
// Return if browser does not implement to keep a consistent interface
if (navigator.mediaDevices.getUserMedia === undefined) {
console.info('getUserMedia is not implemented in this browser');
return;
}
let videoZoneElement = event.srcElement.parentElement;
let vdRecorder = videoZoneElement.querySelector('video');
let constraints = {
audio: true,
video: {
frameRate: { ideal: 10, max: 15 },
facingMode: (hctrl.frontCamera ? "user" : "environment")
}
};
navigator.mediaDevices.getUserMedia(constraints)
.then(function (stream) {
hctrl.vdRecorderSrc = window.URL.createObjectURL(stream);
vdRecorder.src = hctrl.vdRecorderSrc;
let mediaRecorder = new (window as any).MediaRecorder(stream);
hctrl.currentStream = null;
hctrl.recordedBlobs = [];
hctrl.currentStream = stream;
mediaRecorder.start(10);
mediaRecorder.ondataavailable = function (e) {
if (e.data && e.data.size > 0) {
hctrl.recordedBlobs.push(e.data);
}
};
}).catch(function (err) {
console.error("The camera recording request failed with error : " + err);
});
hctrl.showStart = false;
hctrl.showStop = true;
hctrl.showUpload = false;
hctrl.showCameraSelection = false;
}
catch (err) {
console.error("The camera recording request failed with error : " + err);
}
}
/*=================
| Stop Video Recording |
=================*/
private handleVideoStop(event?: any): void {
const hctrl: HomeController = this;
try {
hctrl.currentStream.getTracks().forEach(function (track) {
track.stop();
});
hctrl.recordedBlobType = hctrl.recordedBlobs[0].type;
hctrl.fileBlob = new Blob(hctrl.recordedBlobs, { type: hctrl.recordedBlobType });
let videoZoneElement = event.srcElement.parentElement;
if (videoZoneElement) {
let vdRecorder = videoZoneElement.querySelector('video');
if (vdRecorder) {
if (vdRecorder) {
vdRecorder.pause();
vdRecorder.src = '';
vdRecorder.load();
}
if (hctrl.currentStream && hctrl.currentStream.stop) {
hctrl.currentStream.stop();
}
}
let videoPlaybackZoneElement = videoZoneElement.querySelector('playback');
if (videoPlaybackZoneElement) {
let videoPlaybackElement = document.createElement('video');
videoPlaybackElement.controls = true;
videoPlaybackElement.classList.add(styles.mediaRecorderVideo);
videoPlaybackElement.src = window.URL.createObjectURL(hctrl.fileBlob);
videoPlaybackZoneElement.appendChild(videoPlaybackElement);
videoPlaybackElement.play();
}
}
}
catch (err) {
console.error("The camera stop request failed with error : " + err);
}
finally {
hctrl.currentStream = null;
hctrl.recordedBlobs = [];
hctrl.recordedBlobType = null;
hctrl.vdRecorderSrc = null;
hctrl.showVideo = false;
hctrl.showStart = false;
hctrl.showStop = false;
hctrl.showUpload = true;
hctrl.showRetry = true;
}
}
/*=================
| Chnage Camera |
=================*/
private cameraChange(event?: any): void {
const hctrl: HomeController = this;
try {
if (event.srcElement) {
if (event.srcElement.innerText == "Use Front Camera") {
event.srcElement.innerText = "Use Back Camera";
hctrl.frontCamera = true;
} else {
event.srcElement.innerText = "Use Front Camera";
hctrl.frontCamera = false;
}
}
}
catch (err) {
console.error("The camera change request failed with error : " + err);
}
}
/*=================
| Upload Recording |
=================*/
private handleVideoUpload(event?: any): void {
const hctrl: HomeController = this;
hctrl.showUpload = false;
hctrl.isLoading = true;
try {
let rand = Math.floor((Math.random() * 10000000));
let fileName = "video_" + rand + ".webm";
let contents = hctrl.dataService.arrayBufferToBase64(hctrl.fileBlob);
hctrl.dataService.uploadFile(contents, fileName, hctrl.webUrl, hctrl.listName).then((itemId: string): void => {
hctrl.message = "File uploaded Id is : " + itemId + " with name " + fileName;
}).catch((err): void => {
console.error("The video upload request failed with error : " + err);
});
let videoZoneElement = event.srcElement.parentElement;
if (videoZoneElement) {
let videoPlaybackZoneElement = videoZoneElement.querySelector('playback');
if (videoPlaybackZoneElement) {
let videoPlaybackElement = videoZoneElement.querySelector('video');
if (videoPlaybackElement) {
videoPlaybackElement.parentNode.removeChild(videoPlaybackElement);
}
}
}
}
catch (err) {
console.error("The video upload request failed with error : " + err);
}
finally {
hctrl.fileBlob = null;
hctrl.isLoading = false;
}
}
/*=================
| Upload File |
=================*/
public handleFileUpload(event?: any): void {
const hctrl: HomeController = this;
if (!hctrl.file) {
hctrl.message = 'Select a file to upload.';
return;
}
hctrl.isLoading = true;
let fileName = hctrl.file.fileName;
let fileBuffer = hctrl.file.fileAsBuffer;
try {
hctrl.dataService.uploadFile(fileBuffer, fileName, hctrl.webUrl, hctrl.listName).then((itemId: string): void => {
hctrl.message = "File uploaded Id is : " + itemId + " with name " + fileName;
}).catch((err): void => {
console.error("The file upload request failed with error : " + err);
});
}
catch (err) {
console.error("The file upload request failed with error : " + err);
}
finally {
hctrl.isLoading = false;
hctrl.file = null;
}
}
/*=================
| Retry |
=================*/
public handleVideoRetry(event?: any): void {
const hctrl: HomeController = this;
try {
let videoZoneElement = event.srcElement.parentElement;
if (videoZoneElement) {
let videoPlaybackZoneElement = videoZoneElement.querySelector('playback');
if (videoPlaybackZoneElement) {
let videoPlaybackElement = videoZoneElement.querySelector('video');
if (videoPlaybackElement) {
videoPlaybackElement.parentNode.removeChild(videoPlaybackElement);
}
}
}
hctrl.init(hctrl.listName, hctrl.webUrl);
}
catch (err) {
console.error("The retry request failed with error : " + err);
}
}
}

View File

@ -0,0 +1,4 @@
export interface IFile {
fileName: string;
fileAsBuffer: ArrayBuffer;
}

View File

@ -0,0 +1,11 @@
import * as angular from 'angular';
import HomeController from './HomeController';
import DataService from './DataService';
import { CustomFileChange } from '../app/customFileChange';
const mediarecorderapp: ng.IModule = angular.module('mediarecorderapp', []);
mediarecorderapp
.controller('HomeController', HomeController)
.directive("customFileChange", CustomFileChange.factory())
.service('DataService', DataService);

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Configurations",
"BasicGroupName": "Target",
"ListNameFieldLabel": "Library Name"
}
});

View File

@ -0,0 +1,10 @@
declare interface IMediaRecorderStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
ListNameFieldLabel: string;
}
declare module 'mediaRecorderStrings' {
const strings: IMediaRecorderStrings;
export = strings;
}

View File

@ -0,0 +1,9 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('MediaRecorderWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"types": [
"es6-promise",
"es6-collections",
"webpack-env",
"microsoft-ajax",
"sharepoint"
]
}
}

View File

@ -0,0 +1,11 @@
// Type definitions for Microsoft ODSP projects
// Project: ODSP
/* Global definition for UNIT_TEST builds
Code that is wrapped inside an if(UNIT_TEST) {...}
block will not be included in the final bundle when the
--ship flag is specified */
declare const UNIT_TEST: boolean;
/* Global defintion for SPO builds */
declare const DATACENTER: boolean;

View File

@ -0,0 +1 @@
/// <reference path="@ms/odsp.d.ts" />