Developing for Alexa, how do I display an mp4 video from a private S3 bucket ? (I can do it with mp3 audio using /Utils, but not with the video.) - mp4

When developing on the Alexa, using:
var audioUrl = Util.getS3PreSignedUrl("Media/001.mp3").replace(/&/g,'&');`
I can play an mp3 audio clip using an SSML tag and yet keep the mp3 private, by storing it in an Alexa-hosted S3 bucket (and locking down the permissions to it):
output1+=audio001[currentTrack]+<audio src="${audioUrl1}"/>+moreInstructions; // AND
return handlerInput.responseBuilder
.speak(output1)
.reprompt(moreInstructions)
.getResponse();
However, I can't seem to follow the same approach for an mp4 / video format. It seems that using Alexa Presentation Language (APL), you have to store your videos publically on the internet. I have tried to use the pre-signed Utility function for the Mp4 video, but it doesn't seem to work ...
I tried the following:
const Alexa = require('ask-sdk-core');
const Util = require('./util.js');
const DOCUMENT_ID = "VideoDocument";
var videoUrl = Util.getS3PreSignedUrl("Media/safari.mp4").replace(/&/g,'&');
const datasource = {
"videoPlayerTemplateData": {
"type": "object",
"properties": {
"backgroundImage": "https://d2o906d8ln7ui1.cloudfront.net/images/response_builder/background-green.png",
"displayFullscreen": true,
"headerTitle": "xxx",
"headerSubtitle": "xxx",
"logoUrl": "xxx,
"videoControlType": "skip",
"videoSources": [
// "https://d2o906d8ln7ui1.cloudfront.net/videos/AdobeStock_277864451.mov",
"https://d2o906d8ln7ui1.cloudfront.net/videos/AdobeStock_292807382.mov",
videoUrl
],
"sliderType": "determinate"
}
}
};
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'Welcome, you can say Hello or Help. Which would you like to try?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const createDirectivePayload = (aplDocumentId, dataSources = {}, tokenId = "documentToken") => {
return {
type: "Alexa.Presentation.APL.RenderDocument",
token: tokenId,
document: {
type: "Link",
src: "doc://alexa/apl/documents/" + aplDocumentId
},
datasources: dataSources
}
};
const SampleAPLRequestHandler = {
canHandle(handlerInput) {
// handle named intent
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'HelloWorldIntent';
},
handle(handlerInput) {
if (Alexa.getSupportedInterfaces(handlerInput.requestEnvelope)['Alexa.Presentation.APL']) {
// generate the APL RenderDocument directive that will be returned from your skill
const aplDirective = createDirectivePayload(DOCUMENT_ID, datasource);
// add the RenderDocument directive to the responseBuilder
handlerInput.responseBuilder.addDirective(aplDirective);
}
// send out skill response
return handlerInput.responseBuilder.getResponse();
}
};
It works if videoURL is set to a string or a URL of a publically hosted mp4 video, but not if it points to the pre-signed URL for an mp4 video from an S3 bucket.

The .replace(/&/g,'&') is only used for URLs which will be included in SSML, since this is an XML syntax within a json, so two parsers apply and will correct this when they are invoked inside each other.
If you get a file URL directly in json (what APL is), then you just can use var videoUrl = Util.getS3PreSignedUrl("Media/safari.mp4"); and it should work.

Related

VSCode custom language extention - Show CompletionItems for variable

I'm working on a custom language extension and I'm having issues with how to show functions for a variable.
I'm importing my language in the form of json, so i've created a typescript-file that i import into my extensions.ts:
export interface CustomIntellisense {
text: string;
help: string;
}
const data = [{
"text": "Void.String",
"help": "<h1>String String()</h1><p>Default constructor.<code>String something;</code></p>"
},
{
"text": "String.toInteger",
"help": "<h1>Integer toInteger()</h1><p>Converts a String to its numeric representation."
},
{
"text": "Void.Integer",
"help": "<h1>Integer Integer()</h1><p>Default constructor.</p>"
}];
export let json: CustomIntellisense[] = data;
My idea here is that the elements containing "Void" in text gets created as a variable, while the other element gets added as a method.
const provider1 = vscode.languages.registerCompletionItemProvider({ language: 'myLanguage', scheme: 'file' }, {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
let items: vscode.CompletionItem[] = [];
let re = /\"/gi;
json.forEach(element => {
const item = new vscode.CompletionItem(element.text.split('.')[1]);
item.insertText = new vscode.SnippetString(element.help);
const markdownDocumentation = new vscode.MarkdownString();
markdownDocumentation.supportHtml = true;
markdownDocumentation.appendMarkdown(element.help);
item.documentation = markdownDocumentation;
if (element.text.includes('Void.')) { //If text includes Void this should be a variable
item.kind = vscode.CompletionItemKind.Variable;
}
else {
item.kind = vscode.CompletionItemKind.Method;
}
items.push(item);
});
return items;
}
});
The items gets added to the view, but I can't figure out how to 'filter' what is shown.
The official example on how to achieve this can be found here:
https://github.com/microsoft/vscode-extension-samples/blob/main/completions-sample/src/extension.ts
But this only explains how to filter based on the text/name, and i cant filter this specifically for each variableName i use.. If i somehow could detect what kind of Variable i'm working on I could possibly create a function that fetches if its a String/Int, then parse through my file and add methods in my 2nd CompletionItemProvider. But I havent found any good way of deciding the type of variable..
What i want is this:
If i click ctrl+space i want toInteger() to be the only thing that shows up, but instead it lists up everything all the time:
Anyone have a clue how to achieve this?

How I get contain of a href and img from cgv website using axios and jsdom

I have a problem when i wanto target and get contain a href and img from this website https://www.cgv.id/en/movies/now_playing but i always wrong to get it. This is may code:
const { default: axios } = require("axios");
const { JSDOM } = require("jsdom");
(async () => {
try {
const { data } = await axios.get(
"https://www.cgv.id/en/movies/now_playing"
);
let dom = new JSDOM(data).window.document;
let list = [...dom.getElementsByClassName('movie-list-body').querySelectorAll('li')]
list = list.map(v => v.document.querySelectorAll('li a[href]').textContent.trimEnd())
console.log(list);
} catch (error) {
console.log(error);
}
})()
My code is error. How i repair it and can target to get contain a href and img it?
There are couple of issues with using JSDOM there, especially the way you are using it.
Firstly the website in question does not have any markup for the DOM element with the class name movie-list-body as you retrieve it using Axios
On further inspection I realised they are using a jQuery AJAX call to retrieve all the links and images from a JSON file.
Following is the script they are using to do so.
<script>
$(function() {
$.ajax({
type: 'GET',
url: '/en/loader/home_movie_list',
success: function(data) {
$('.movie-list-body').html(data.now_playing);
$('.comingsoon-movie-list-body').html(data.comingsoon);
$('.lazy').lazy({
combined: true
});
}
});
});
</script>
In my opinion you should just use that JSON file. However, if you still want to use JSDOM following are some of the approaches.
Given that the site requires resource processing, if you want to parse the whole page using JSDOM you will have to pass the options as mentioned in the JSDOM documentation as follows:
const options = {
contentType: "text/html",
includeNodeLocations: true,
resources: "usable",
};
let dom = new JSDOM( data, options ).window.document;
These options will allow the JSDOM to load all the resources including jQuery that will in-turn allow the Node to make the AJAX call, populate the element and then in-theory you extract the links. However, there are some CSS files that JSDOM is unable to parse.
Therefore, I think your best bet is to do something along the following lines:
const { default: axios } = require("axios");
const { JSDOM } = require("jsdom");
(async () => {
try {
const data = await axios.get(
"https://www.cgv.id/en/loader/home_movie_list"
);
const base_url = 'https://www.cgv.id';
var dom = new JSDOM(data.data.now_playing).window.document;
var lists = [ ... dom.getElementsByTagName('ul')[0].children ]
var list = lists.map( list => [ base_url+list.firstChild.href, list.firstChild.firstChild.dataset.src ] );
console.log( list );
} catch (error) {
console.log(error);
}
})()
Note:
There is only one catch with the approach mentioned above which is that if the author of the website changes the endpoint for the JSON file, your solution will stop working.

How could I load video files from my library? Ionic 3

I observed that the Native File has not been supported by the Ionic View anymore see list here.
I am trying to get a video from my library by using Native Camera to access the videos. It can return me 3 different formats of path to my videos (DATA_URL, FILE_URI, and NATIVE_URI).reference to Native Camera here
I am currently using FILE_URI as recommended in this post. It returns something like "/storage/emulated/0/DCIM/Camera/VID_20180312_210545.mp4"
Please have a look at my code below. Aiming a better understanding, the current behavior is highlighted by comments with "//** comment ***" :
addVideoToOffer(){
this.platform.ready().then(() =>{
const options: CameraOptions = {
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
destinationType: this.camera.DestinationType.FILE_URI,
mediaType: this.camera.MediaType.VIDEO,
}
this.camera.getPicture(options).then((data_uri) => {
this.readVideoFileasGeneral(data_uri);
});
});
}
readVideoFileasGeneral(data_uri) {
if(!data_uri.includes('file://')) {
data_uri = 'file://' + data_uri;
}
return this.file.resolveLocalFilesystemUrl(data_uri)
.then((entry: FileEntry) => {
//***it does not get in here***
this.presentQuickToastMessage(data_uri);
return new Promise((resolve)=>{//, reject) => {
entry.file((file) => {
let fileReader = new FileReader();
fileReader.onloadend = () => {
let blob = new Blob([fileReader.result], {type: file.type});
resolve({blob: blob, file: file});
};
fileReader.readAsArrayBuffer(file);
});
})
})
.catch((error) => {
this.presentQuickToastMessage(error);
//***it presents "plugin_not_installed" here***
});
}
I understand that I am having this message because Native File is not supported anymore (maybe reason of the plugin_not_installed message). However, I still have to do this task. So, if someone has any idea of what I could be using in order to have the selected videos in a blob, it would be great!
Thanks for reading until here,
Cheers,
Roger A L
makeFileIntoBlob(uri) {
// get the correct path for resolve device file system
let pathIndex = uri.indexOf('var');
let correctPath = uri.slice(+pathIndex);
this.file
.resolveLocalFilesystemUrl((this.platform.is('ios') ? 'file:///' : '') + correctPath)
.then(entry => (<FileEntry>entry).file(file => this.readFile(file)))
.catch(err => console.log('ERROR: ', err));
}
readFile(file) {
if(file) {
const reader = new FileReader();
reader.onloadend = () => {
const blob: any = new Blob([reader.result], { type: file.type });
blob.name = file.name;
console.log(blob);
return blob;
};
reader.readAsArrayBuffer(file);
}
}
You need to get rid of the /private/ and keep file:///, so that your path goes like file:///var/
I'm currently working on something similar.. I have the video recorded with media-capture and then I can display it within a normal video html tag.. if this is all you need then this code may help you...
this.mediaCapture.captureVideo({duration: 10, quality: 0}).then(
(data: MediaFile[]) => {
if (data.length > 0) {
let originname = data[0].fullPath.substr(data[0].fullPath.lastIndexOf('/') + 1);
let originpath = data[0].fullPath.substr(0, data[0].fullPath.lastIndexOf('/') + 1);
let alerta = this.alerts.create({
buttons: ['ok'],
message: this.file.externalDataDirectory
});
alerta.then(set => set.present());
this.file.copyFile(originpath, originname, this.file.externalDataDirectory, 'video.mp4')
.then(result =>{
let videopath = this.webview.convertFileSrc(result.nativeURL)
let video = (document.getElementById('myvideo') as HTMLVideoElement).src = videopath;
.... rest of the code
The problem raise when you try to use the native File plugin... converting files with any method (readAsDataURL, readAsArrayBuffer or readAsBinaryString) will never resolve, this is a known problem with the Ionic Native File plugin but is not taken care of...
What I did is to take the ionic native Filesystem and use it to read the file, this does read the file and get you with a base64 (pretty sure as I don't specify the encoding field) and then you can handle it the way you want...
const data = Filesystem.readFile({
path: result.nativeURL
})
.then(data =>{
...handle data as base64
...rest of the code

Store image to Firebase storage from cordova camera plugins on Ionic

I've red some topics on the subject (e.g: Uploading image to Firebase Storage from Cordova app) but didn't find my answer...
I'm working on a IONIC project with the implementation of the ngCordova camera plugin to take picture and get pic from the librairy.
So I got the result as a image URI and I want to upload it in Firebase storage (as file or Blob).
Here is my code :
$scope.fromCamera = function() {
$ionicPlatform.ready(function() {
var options = {
quality: 75,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 300,
targetHeight: 300,
saveToPhotoAlbum: true e
};
$cordovaCamera.getPicture(options).then(function(imageURI) {
window.resolveLocalFileSystemURL(imageURI, function (fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function () {
// This blob object can be saved to firebase
var blob = new Blob([new Uint8Array(this.result)], { type: "image/jpeg" });
// Create the storage ref
var ref = storageRef.child('images/test');
// Upload the file
uploadPhoto(blob, ref);
};
reader.readAsArrayBuffer(file);
});
}, function (error) {
console.log(error)
});
});
});
};
I read the file and convert it into a Blob before uploading it into firebase. And I got an 'Encoding error' telling me "A URI supplied to the API was malformed, or the resulting Data URL has exceeded the URL length limitations for Data URLs."
I'm running it an chrome browser with the Cordova Mocks extension.
Any help is welcome!
Thanks
uploadPhoto() is my function to upload the file on firebase storage (and save the URL in firebase database)
var storageRef = Firebase.storageRef();
var databaseRef = Firebase.databaseRef();
var uploadPhoto = function(file, ref) {
var task = ref.put(file);
// Update progress bar
task.on('state_changed', function(snapshot){
// nothing
}, function(error) {
// Handle unsuccessful uploads
}, function() {
// Handle successful uploads on complete
$scope.downloadURL = task.snapshot.downloadURL;
$scope.actualKey = databaseRef.child('posts').push().key;
databaseRef.child('posts/' + $scope.actualKey).update({
url : $scope.downloadURL,
id : $scope.actualKey,
time : firebase.database.ServerValue.TIMESTAMP,
});
}
);
}
try changing...
[new Uint8Array(this.result)]
to just this
[this.result]
alternate approach using $cordovaFile
var fileName = imageURI.replace(/^.*[\\\/]/, '');
$cordovaFile.readAsArrayBuffer(cordova.file.tempDirectory, fileName)
.then(function (success) {
// success - get blob data
var imageBlob = new Blob([success], { type: "image/jpeg" });
// Create the storage ref
var ref = storageRef.child('images/test');
// Upload the file
uploadPhoto(imageBlob, ref);
}, function (error) {
// error
});
Instead of getting the path from the URI, in the code, I assume the following...
// modify the image path when on Android
if ($ionicPlatform.is("android")) {
path = cordova.file.cacheDirectory
} else {
path = cordova.file.tempDirectory
}
feel free to parse the path to get the directory

Image uploaded from Mobile phone to Angular is sideways or upside down

I am able to upload images from my desktop to an Angular based Web Application overlayed on SharePoint without issue, but if I upload from a Mobile phone, such as an iPhone, using the take "Take Photo or Video" or "Photo Library" function, it causes the image to be sideways when taken in portrait or upside down when taken in landscape. Here is my current upload function. Any clues/have others had the same issues uploading to Mobile Web Applications from iPhones/Mobile Phones to a SharePoint library?
Here is my upload function:
// Upload of images
$scope.upload = function () {
//console.log($scope.files);
if (document.getElementById("file").files.length === 0) {
alert('No file was selected');
return;
}
var parts = document.getElementById("file").value.split("\\");
var uploadedfilename = parts[parts.length - 1];
var basefilename = uploadedfilename.split(".")[0];
var fileextension = uploadedfilename.split(".")[1];
var currentdate = new Date();
var formatteddate = $filter('date')(new Date(currentdate), 'MMddyy-hmmssa');
var filename = basefilename + formatteddate + '.' + fileextension;
var file = document.getElementById("file").files[0];
uploadFileSync("/sites/asite", "Images", filename, file);
}
//Upload file synchronously
function uploadFileSync(spWebUrl, library, filename, file)
{
console.log(filename);
var reader = new FileReader();
reader.onloadend = function(evt)
{
if (evt.target.readyState == FileReader.DONE)
{
var buffer = evt.target.result;
var completeUrl = spWebUrl
+ "/_api/web/lists/getByTitle('"+ library +"')"
+ "/RootFolder/Files/add(url='"+ filename +"',overwrite='true')?"
+ "#TargetLibrary='"+library+"'&#TargetFileName='"+ filename +"'";
$.ajax({
url: completeUrl,
type: "POST",
data: buffer,
async: false,
processData: false,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"content-length": buffer.byteLength
},
complete: function (data) {
console.log(data);
},
error: function (err) {
alert('failed');
}
});
}
};
reader.readAsArrayBuffer(file);
}
The output of these is just pushed into an array for use in an Angular UI Carousel:
// Control of Image Carousel
$scope.myInterval = 0;
// Population of carousel
$scope.slides = [];
appImages.query({
$select: 'FileLeafRef,ID,Created,Title,UniqueId',
$filter: 'ReportId eq ' + $routeParams.Id + ' and DisplayinReport eq 1',
}, function (getimageinfo) {
// Data is within an object of "value"
var image = getimageinfo.value;
// Iterate over item and get ID
angular.forEach(image, function (imagevalue, imagekey) {
$scope.slides.push({
image: '/sites/asite/Images/' + imagevalue.FileLeafRef,
});
});
});
The image carousel is on page as follows:
<div style="height: 305px; width: 300px">
<carousel interval="myInterval">
<slide ng-repeat="slide in slides" active="slide.active">
<img ng-src="{{slide.image}}" style="margin:auto;height:300px">
<div class="carousel-caption">
<h4>Slide {{$index}}</h4>
<p>{{slide.text}}</p>
</div>
</slide>
</carousel>
</div>
IMPORTANT: The images are sideways and upside down upon upload to the SharePoint library, so irrespective of outputting them, they seem to be misoriented when they hit the destination library I am using as a source to display on page.
How do I upload the images so SharePoint respects the EXIF data/orientation?
It may be related to EXIF. See JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images
If you want a better answer, we will need the code which show the image, and the code server side.
UPDATE : I'm not an expert at all on SharePoint, but you can found a lot about it in the SharePoint Stack Exchange. For example, https://sharepoint.stackexchange.com/questions/131552/sharepoint-rotating-pictures-in-library, should do the trick.
To sum up a little : in your case, their could be a lot of cases to study. So, I recommended you auto-correct the exif, and then permit to your user to correct it if the auto-correct was wrong. Their is a lot of tools to do that. If you want to do it server-side, look at the link above, and if you want to do it on the client side, you could use JS-Load-Image for example.