How to preload audio files in react native? - javascript

I am working on a media player right now but I am keen to find a solution where I can preload first 4 audio files and when I am going to next one the function with preload will run again. I am using an array with url path for each that points to s3 bucket not locally. I also want to mention that I am using expo-av package
For now the loading looks like this
componentDidMount() {
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
staysActiveInBackground: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false
});
}
async _loadNewPlaybackInstance(playing) {
if (this.playbackInstance != null) {
await this.playbackInstance.unloadAsync();
this.playbackInstance = null;
}
const source = { uri: PLAYLIST[this.index].url };
const initialStatus = {
shouldPlay: playing,
rate: this.state.rate,
shouldCorrectPitch: this.state.shouldCorrectPitch,
volume: this.state.volume,
};
const { sound, status } = await Audio.Sound.createAsync(
source,
initialStatus,
this._onPlaybackStatusUpdate
);
this.playbackInstance = sound;
if (status) {
this._onPlayPausePressed()
}
this._updateScreenForLoading(false);
}

Related

How do I refactor my code for Playing next Video in React app

In my React, on different pages, I have video teasers where you can click on. Those teasers has a <Link> where I send the uuid of the video in a query param.
Based on the uuid (I grep from the query param), I can query the correct video:
With this GraphQl query, I query the video based on uuid:
const { data } = useQuery<GqlResponse, VideoArgs>(GET_VIDEO, {
variables: {
uuid: id,
},
errorPolicy: 'all',
});
Then the correct video is played in an overlay. In the same overlay I use a different GraphQl query to return a list of video teasers (its always the same list in every overlay):
The list of videos in the sidebar:
const { previousData, data: myVideos = previousData, loading, fetchMore } = useQuery<
GqlResponse,
Args
>(GET_VIDEOS, {
errorPolicy: 'all',
notifyOnNetworkStatusChange: true,
});
I want to pass the nextVideo to the player. I got this working with the following code:
const renderNextVideo = () => {
const findVideoIndex = (item: VideoTeaserType) => item.uuid === id;
if (findVideoIndex === undefined) {
return myVideos?.videos.items[0];
}
return myVideos?.videos.items[
myVideos?.videos.items.findIndex(findVideoIndex) + 1
];
};
Above code is checking when you clicked on a video from outside of the overlay findVideoIndex is undefined so the next video is the first array item.
Is this a clean approach or can I refactor it and make it cleaner?
For example:
inside my onVideoEnd function, I need to push it now like:
push(renderNextVideo()?.uuid!);
but I want to have something like:
push(nextVideo?.uuid!);

VideoJS not playing pre-signed URL in mp4 format

I'm using videojs in a vue application to play a pre-signed URL for a mp4 video file on S3. I'm using the pre-signed URL as the source of the videojs player but I get the error
The media could not be loaded, either because the server or network
failed or because the format is not supported.
This is how my URL looks like:
https://bucket-name.s3.region.amazonaws.com/object.mp4?AWSAccessKeyId=xxxxxxxxxxxx&Expires=xxxxxxx&Signature=xxxxxx&x-amz-security-token=xxxxxxx
I looked at similar questions on SO and someone suggested to change the URL format to below format, but that didn't work either.
https://s3.amazonaws.com/bucket-name/object.mp4?AWSAccessKeyId=xxxxxxxxxxxx&Expires=xxxxxxx&Signature=xxxxxx&x-amz-security-token=xxxxxxx
The video player plays the video if I put either of the above hardcoded URLs as its source, but not when I do as variables.
Why is it changing behaviour when a variable is used ?
<template>
<div>
<video-player
:options="{
autoplay: false,
controls: true,
sources: [
{
src: `${URL}`,
type: 'video/mp4',
},
],
}"
/>
</div>
</template>
<script>
import VideoPlayer from '#/components/VideoPlayer.vue';
var AWS = require('aws-sdk');
var bucketName = 'xxxx';
var s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: { Bucket: bucketName },
});
export default {
components: {
VideoPlayer,
},
data() {
return {
URL: String,
};
},
methods: {
getURL() {
var self = this;
let myPromise = new Promise(function (myResolve, myReject) {
const url = s3.getSignedUrl('getObject', {
Key: `xxxxx.mp4`,
Expires: 3600,
});
myResolve(url);
myReject('sorry, error');
});
myPromise.then(
function (value) {
self.URL = value;
},
function (error) {
console.log(error);
}
);
},
},
mounted() {
this.getURL();
},
};
</script>
The issue was that the URL value was not being set as the source, as mentioned by #misterben.
I made the following changes to set the source in the method. Although there might be better ways of setting the source in Vue ( other than using querySelector)
<video-player
:options="{
autoplay: false,
controls: true,
}"
/>
</div>
and,
import videojs from 'video.js';
getSignedUrl() {
// this is because the imported videoPlayer component has class="video-js"
var player = videojs(document.querySelector('.video-js'));
var self = this;
var params = {
Bucket: 'xxxxx',
Key: `xxxxx`,
};
var promise = s3.getSignedUrlPromise('getObject', params);
promise.then(
async function (url) {
self.URL = url;
await player.src({
src: url,
type: 'video/mp4' /*video type*/,
});
},
function (err) {
console.log(err);
}
);
},

Ionic Capacitor cordova plugin unable to write image to library on Android

I am trying to use cordova-plugin-document-scanner with Ionic Capacitor for Android, but after the image is captured and when image crop UI should be displayed, it just returns to the capture screen again. This is the issue on the github repo. This is what seems relevant in the logcat console:
2020-08-03 13:26:05.320 27707-27707/si.test.app D/ViewRootImpl#9f2e8cd[ScanActivity]: Relayout returned: old=(0,0,1080,2280) new=(0,0,1080,2280) req=(1080,2280)4 dur=8 res=0x1 s={false 0} ch=false 2020-08-03 13:26:05.321 27707-27707/si.test.app D/ViewRootImpl#9f2e8cd[ScanActivity]: stopped(false) old=true 2020-08-03 13:26:05.328 27707-27707/si.test.app W/System.err: java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
2020-08-03 13:42:50.749 1278-1278/? E/Util: writeImageDataToRequestedUri : failed to make directory or the directory already existed.
2020-08-03 13:42:50.760 1278-1278/? E/Util: writeImageDataToRequestedUri : Returned because outputStream IOException.
This is my configuration:
Device: Samsung Galaxy S10e
OS: Android 10
#capacitor/core : 2.3.0
#capacitor/android: 2.3.0
cordova-plugin-document-scanner: 4.2.5
Is it possible, that Cordova and Capacitor have different paths to files on the device? Where could I fix that? Any help greatly appriciated :)
I implemented this in my project using Ionic 5 and Capacitor. It is long code. Try this and maybe help you.
Install this npms
npm install cordova-plugin-crop
npm install #ionic-native/crop
npm install cordova-plugin-ionic-webview
npm install #ionic-native/ionic-webview
Then create service file.
ex: photo.service
Then add below code according to your case. I added my full code to here because it include all things.
There are two method.
getImageCam() - get image from camera > source: CameraSource.Camera
getImageGall() - get image from gallery > source: CameraSource.Photos
import { Injectable } from "#angular/core";
import {
Plugins,
CameraResultType,
CameraPhoto,
CameraSource,
} from "#capacitor/core";
import { Crop } from "#ionic-native/crop/ngx";
import { WebView } from "#ionic-native/ionic-webview/ngx";
//import { File } from "#ionic-native/file/ngx";
const { Camera, Filesystem, Storage } = Plugins;
#Injectable({
providedIn: "root",
})
export class PhotoService {
newCapturedImg: any = null;
ImgNameStart: any = "yourName";
formDataImage: any;
cropImage: CameraPhoto;
constructor(private crop: Crop, private webview: WebView) {}
public async getImageCam() {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Camera,
quality: 100,
// allowEditing: true,
// height: 300,
// width: 300
});
console.log(capturedPhoto);
this.crop
.crop(capturedPhoto.path, {
quality: 100,
})
.then(
(newImage) => {
this.newCapturedImg = this.webview.convertFileSrc(newImage);
//console.log("new image path is: " + newImage);
//console.log("new image webpath is: " + this.newCapturedImg);
this.cropImage = {
path: newImage,
webPath: this.newCapturedImg,
format: "jpeg",
};
const savedImageFile = this.savePicture(this.cropImage);
},
(error) => console.error("Error cropping image", error)
);
}
public async getImageGall() {
// Take a photo
const capturedPhoto = await Camera.getPhoto({
resultType: CameraResultType.Uri,
source: CameraSource.Photos,
quality: 100,
// allowEditing: true,
// height: 300,
// width: 300,
});
this.crop
.crop(capturedPhoto.path, {
quality: 100,
})
.then(
(newImage) => {
this.newCapturedImg = this.webview.convertFileSrc(newImage);
//console.log("new image path is: " + newImage);
//console.log(this.newCapturedImg);
this.cropImage = {
path: newImage,
webPath: this.newCapturedImg,
format: "jpeg",
};
const savedImageFile = this.savePicture(this.cropImage);
},
(error) => console.error("Error cropping image", error)
);
}
private async savePicture(cameraPhoto: CameraPhoto) {
const blobData = await this.readABlob(cameraPhoto);
this.formDataImage = blobData;
}
private async readABlob(cameraPhoto: CameraPhoto) {
const response = await fetch(cameraPhoto.webPath!);
const blob = await response.blob();
console.log("blob --> ", blob);
return blob;
}
createFileName() {
let d = new Date();
let n = d.getTime();
let newFileName = `${this.ImgNameStart + n}.jpg`;
return newFileName;
}
}
interface Photo {
filepath: string;
webviewPath: string;
base64?: string;
}
You can access service variable like this from any component.
example.page.ts
import { PhotoService } from "../../services/photo.service";
...
constructor(public photoService: PhotoService) {}
...
yourMethod() {
this.photoService.getImageCam() // or getImageGall()
let formDataImage = this.photoService.formDataImage;
let imageName = this.photoService.createFileName();
let urlToImageSrc = this.photoService.newCapturedImg;
}

Using the node.js google cloud speech to text, how can I get the status of a current job?

I managed to trigger a job with:
const config = {
languageCode: 'en-US',
enableSpeakerDiarization: true,
audioChannelCount: 2,
enableSeparateRecognitionPerChannel: true,
useEnhanced: true,
profanityFilter: false,
enableAutomaticPunctuation: true,
};
const audio = {
uri: `gs://${filePath}`
}
const requestObj = {
config: config,
audio: audio
}
return speechClient.longRunningRecognize(requestObj)
I get back an object with a name. I want to use that with https://cloud.google.com/speech-to-text/docs/reference/rest/v1/LongRunningRecognizeMetadata (via the node.js package) to get the current status.
How do I do it?
return speechClient.longrunning.Operation()
Seems not to exist
Looks like you can do it with:
return speechClient.operationsClient.getOperation({ name: googleName })
This is not super well documented

Node.js using amazon transcoder to format video / audio files

My goal is to make sure that all videos that are being uploaded to my application is the right format and that they are formatted to fit minimum size.
I did this before using ffmpeg however i have recently moved my application to an amazon server.
This gives me the option to use Amazon Elastic Transcoder
However by the looks of it from the interface i am unable to set up automatic jobs that look for video or audio files and converts them.
For this i have been looking at their SDK / api references but i am not quite sure how to use that in my application.
My question is has anyone successfully started transcoding jobs in node.js and know how to convert videos from one format to another and / or down set the bitrate? I would really appreciate it if someone could point me in the right direction with some examples of how this might work.
However by the looks of it from the interface i am unable to set up
automatic jobs that look for video or audio files and converts them.
The Node.js SDK doesn't support it but you can do the followings: if you store the videos in S3 (if not move them to S3 because elastic transcoder uses S3) you can run a Lambda function on S3 putObject triggered by AWS.
http://docs.aws.amazon.com/lambda/latest/dg/with-s3.html
My question is has anyone successfully started transcoding jobs in
node.js and know how to convert videos from one format to another and
/ or down set the bitrate? I would really appreciate it if someone
could point me in the right direction with some examples of how this
might work.
We used AWS for video transcoding with node without any problem. It was time consuming to find out every parameter, but I hope these few line could help you:
const aws = require('aws-sdk');
aws.config.update({
accessKeyId: config.AWS.accessKeyId,
secretAccessKey: config.AWS.secretAccessKey,
region: config.AWS.region
});
var transcoder = new aws.ElasticTranscoder();
let transcodeVideo = function (key, callback) {
// presets: http://docs.aws.amazon.com/elastictranscoder/latest/developerguide/system-presets.html
let params = {
PipelineId: config.AWS.transcode.video.pipelineId, // specifies output/input buckets in S3
Input: {
Key: key,
},
OutputKeyPrefix: config.AWS.transcode.video.outputKeyPrefix,
Outputs: config.AWS.transcode.video.presets.map(p => {
return {Key: `${key}${p.suffix}`, PresetId: p.presetId};
})
};
params.Outputs[0].ThumbnailPattern = `${key}-{count}`;
transcoder.createJob(params, function (err, data) {
if (!!err) {
logger.err(err);
return;
}
let jobId = data.Job.Id;
logger.info('AWS transcoder job created (' + jobId + ')');
transcoder.waitFor('jobComplete', {Id: jobId}, callback);
});
};
An example configuration file:
let config = {
accessKeyId: '',
secretAccessKey: '',
region: '',
videoBucket: 'blabla-media',
transcode: {
video: {
pipelineId: '1450364128039-xcv57g',
outputKeyPrefix: 'transcoded/', // put the video into the transcoded folder
presets: [ // Comes from AWS console
{presetId: '1351620000001-000040', suffix: '_360'},
{presetId: '1351620000001-000020', suffix: '_480'}
]
}
}
};
If you want to generate master playlist you can do it like this.
".ts" files can not playable via hls players. Generate ".m3u8" file
async function transcodeVideo(mp4Location, outputLocation) {
let params = {
PipelineId: elasticTranscoderPipelineId,
Input: {
Key: mp4Location,
AspectRatio: 'auto',
FrameRate: 'auto',
Resolution: 'auto',
Container: 'auto',
Interlaced: 'auto'
},
OutputKeyPrefix: outputLocation + "/",
Outputs: [
{
Key: "hls2000",
PresetId: "1351620000001-200010",
SegmentDuration: "10"
},
{
Key: "hls1500",
PresetId: "1351620000001-200020",
SegmentDuration: "10"
}
],
Playlists: [
{
Format: 'HLSv3',
Name: 'hls',
OutputKeys: [
"hls2000",
"hls1500"
]
},
],
};
let jobData = await createJob(params);
return jobData.Job.Id;
}
async function createJob(params) {
return new Promise((resolve, reject) => {
transcoder.createJob(params, function (err, data) {
if(err) return reject("err: " + err);
if(data) {
return resolve(data);
}
});
});
}

Categories