how to implement cache in react-native-video - react-native

How do we implement caching in react-native-video? Basically, when a video is currently streaming from a network resource, how do we save the video somewhere, and then retrieve it when the same resource is access. What is the best approach for this?

The best approach that i would refer you is using react-native-fetch-blob, you can implement it like this:
const RNFetchBlob = require('react-native-fetch-blob').default;
const {
fs
} = RNFetchBlob;
const baseCacheDir = fs.dirs.CacheDir + '/videocache';
//call the downloadVideo function
downloadVideo('http://....',baseCacheDir)
//Function to download a file..
const activeDownloads = {};
function downloadVideo(fromUrl, toFile) {
// use toFile as the key
activeDownloads[toFile] = new Promise((resolve, reject) => {
RNFetchBlob
.config({path: toFile})
.fetch('GET', fromUrl)
.then(res => {
if (Math.floor(res.respInfo.status / 100) !== 2) {
throw new Error('Failed to successfully download video');
}
resolve(toFile);
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err));
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
return activeDownloads[toFile];
}
//To delete a file..
function deleteFile(filePath) {
return fs.stat(filePath)
.then(res => res && res.type === 'file')
.then(exists => exists && fs.unlink(filePath)) //if file exist
.catch((err) => {
// swallow error to always resolve
});
}
Cheers:)

Related

Can't perform a React state update on an unmounted component. useEffect Hook

I seem to be missing something subtle about avoiding memory leaks. I have read a few posts on how to avoid this with async functions and have tried a few things. All seem to fail. Could someone point out what I'm doing wrong.
useEffect(() => {
let ignore = false;
if (Platform.OS === "android" && !Constants.isDevice) {
errorMessage("Oops, this will not work on Sketch in an Android emulator. Try it on your device!");
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
setErrorMessage("Permission to access location was denied");
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`)
.then(res => res.json())
.then(json => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`)
.then(res => res.json())
.then(json => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
}
getDataAsync();
return () => { ignore = true; }
}
}, []);
I'm getting the error when deliberately quickly switching out of the screen and back again while the activity indicator is spinning.
Return the cleanup outside of everything! let me know if it works
useEffect(() => {
let ignore = false;
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`,
)
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`,
)
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
};
getDataAsync();
}
return () => {
ignore = true;
};
}, []);
That was promising, but no, it didn't work. It may have to do with the fact that there are 2 async fetch requests and one "await" location request with each taking a different amount of time.
I am trying with abortController but that isn't working either:
useEffect(() => {
const abortController = new AbortController();
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
let location = await Location.getCurrentPositionAsync({signal: abortController.signal});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
};
getDataAsync();
}
return () => {
abortController.abort();
}
}, []);
In addition to the memory leak error in the console, I am also getting:
Possible Unhandled Promise Rejection (id: 0):
[AbortError: Aborted]
Possible Unhandled Promise Rejection (id: 1):
[AbortError: Aborted]

stream s3 to dynamodb with fast-csv : not all data inserted

When a csv file is uploaded on my s3 bucket, my lambda will be triggered to insert my data into DynamoDB.
I need a stream because the file is too large to be downloaded as full object.
const batchWrite = async (clientDynamoDB, itemsToProcess) => {
const ri = {};
ri[TABLE_DYNAMO] = itemsToProcess.map((itm) => toPutRequest(itm));
const params = { RequestItems: ri };
await clientDynamoDB.batchWriteItem(params).promise();
};
function runStreamPromiseAsync(stream, clientDynamoDB) {
return new Promise((resolve, reject) => {
const sizeChunk = 25;
let itemsToProcess = [];
stream
.pipe(fastCsv.parse({headers: Object.keys(schemaGeData), trim: true}))
.on("data", (row) => {
stream.pause();
itemsToProcess.push(row);
if (itemsToProcess.length === sizeChunk) {
batchWrite(clientDynamoDB, itemsToProcess).finally(() => {
stream.resume();
});
itemsToProcess = [];
}
})
.on("error", (err) => {
console.log(err);
reject("Error");
})
.on("end", () => {
stream.pause();
console.log("end");
batchWrite(clientDynamoDB, itemsToProcess).finally(() => {
resolve("OK");
});
});
});
}
module.exports.main = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const object = event.Records[0].s3;
const bucket = object.bucket.name;
const file = object.object.key;
const agent = new https.Agent({
keepAlive: true
});
const client = new AWS.DynamoDB({
httpOptions: {
agent
}
});
try {
//get Stream csv data
const stream = s3
.getObject({
Bucket: bucket,
Key: file
})
.createReadStream()
.on('error', (e) => {
console.log(e);
});
await runStreamPromiseAsync(stream, client);
} catch (e) {
console.log(e);
}
};
When my file is 1000 lines everything is inserted but when I have 5000 lines, my function insert only around 3000 lines and this number is random... Sometimes more sometimes less..
So I'd like to understand what am I missing here ?
I also read this article but to be honest even if you pause the second stream, the first one is still running.. So if someone have any ideas on how to do this, it would be greatly appreciated !
Thanks
I found out why It was not fully processed, it's because the callback of batchWriteItem can return unprocess Items. So I change the function batchWrite and also the runPromiseStreamAsync a little bit because i might not have all the items processed from itemsToProcess.
Anyway here is the full code :
const batchWrite = (client, itemsToProcess) => {
const ri = {};
ri[TABLE_DYNAMO] = itemsToProcess.map((itm) => toPutRequest(itm));
const items = { RequestItems: ri };
const processItemsCallback = function(err, data) {
return new Promise((resolve, reject) => {
if(!data || data.length === 0){
return resolve();
}
if(err){
return reject(err);
}
let params = {};
params.RequestItems = data.UnprocessedItems;
return client.batchWriteItem(params, processItemsCallback);
});
};
return client.batchWriteItem(items, processItemsCallback );
};
function runStreamPromiseAsync(stream, clientDynamoDB) {
return new Promise((resolve, reject) => {
const sizeChunk = 25;
let itemsToProcess = [];
let arrayPromise = [];
stream
.pipe(fastCsv.parse({headers: Object.keys(schemaGeData), trim: true}))
.on("error", (err) => {
console.log(err);
reject("Error");
})
.on('data', data => {
itemsToProcess.push(data);
if(itemsToProcess.length === sizeChunk){
arrayPromise.push(batchWrite(clientDynamoDB, itemsToProcess));
itemsToProcess = [];
}
})
.on('end', () => {
if(itemsToProcess.length !== 0){
arrayPromise.push(batchWrite(clientDynamoDB, itemsToProcess));
}
resolve(Promise.all(arrayPromise).catch(e => {
reject(e)
}));
});
});
}

How to do Reverse Geocoding in react native

I am trying to get my current location in react native, using react-native-geolocation I get latitude and longitude of my location. Now I want to convert them into the location's address without using the Google API key.
Is there any way to convert latitude longitude into an address without using the Google API key?
There are many ways to convert lon/lat to address without using Google Maps API. Search reverse geocoding api and you'll find a bunch of alternatives.
A few months ago I was being overcharged by Google for reverse geocoding API requests. So I switched to Here. They have a free tier that offers 250k requests/months, which works for my app. See the docs here: https://developer.here.com/documentation/examples/rest/geocoder/reverse-geocode
This will give you highly detailed address data (unlike ip-api.com suggested by Muhammad).
Here is the wrapper function I use to call the API:
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
// the response had a deeply nested structure :/
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
resolve()
}
})
.catch((e) => {
console.log('Error in getAddressFromCoordinates', e)
resolve()
})
})
}
there are many alternatives you can search reverse geocoding API
Solution 1:
By using Google map key
const myApiKey="Key Received from Google map"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
fetch(
'https://maps.googleapis.com/maps/api/geocode/json?address=' +
latitude +
',' +
longitude +
'&key=' +
myApiKey,
)
.then(response => response.json())
.then(responseJson => {
if (responseJson.status === 'OK') {
resolve(responseJson?.results?.[0]?.formatted_address);
} else {
reject('not found');
}
})
.catch(error => {
reject(error);
});
});
}
Solution 2:
By using Here Plateform key
https://developer.here.com/documentation/geocoder/dev_guide/topics/example-reverse-geocoding.html
They have a free tier which gives us 250k requests/months free quota
const HERE_API_KEY="Key Received from Here Plateform"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
reject('not found')
}
})
.catch((e) => {
reject(e);
})
})
}
Solution :
const getAddressFromCoordinates = async(latitude, longitude) => {
try {
const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=key`);
const json = await response.json();
setRealAddress(json.results[0]?.formatted_address);
return json?.results[0]
// return json.movies;
} catch (error) {
console.error(error);
}
}
I just ran into the same issue with my react native project and then came here for answers, the first upvoted answer really helped me but I couldn't get it to work on my end so I went to their website and read their docs and it seems they updated the API's response, I was able to get it to work from the updated response and the code is below :
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve, reject) => {
const url = `https://revgeocode.search.hereapi.com/v1/revgeocodeat=${latitude}%2C${longitude}&lang=en-US&apiKey=${HERE_API_KEY}`;
fetch(url)
.then(res => res.json())
.then(resJson => {
if (resJson.items[0].title) {
resolve(resJson.items[0].address.label);
} else {
reject('not found');
}
})
.catch(e => {
reject(e);
});
});
}

reaact-native get data from file from uri

This is a homework type question, please help as I'm new on react-native.
In react-native, I'm using react-native-document-picker, from the code in readme:
selectFiles = () => {
try {
DocumentPicker.pickMultiple({
type: [DocumentPicker.types.allFiles],
}).then((results) => {
console.log(result[0].uri);
}
});
} catch (err) { }
};
It provides me with an URI, how can convert that into path and read data of that file ?
Here is my solution :
var RNFS = require('react-native-fs');
...
RNFS.readFile(results[0].uri)
.then((file) => {
that.setState({code: file});
})
.catch((error) => console.log('err: ' + error));

Image upload using react-admin

I am new to react-admin. I am using react-admin to upload the file. I have following the step mentioned below in tutorial.
But after I submit the request...I see http trace as follow. I see blob link instead of Base64 image payload.
{
"pictures": {
"rawFile": {
"preview": "blob:http://127.0.0.1:3000/fedcd180-cdc4-44df-b8c9-5c7196788dc6"
},
"src": "blob:http://127.0.0.1:3000/fedcd180-cdc4-44df-b8c9-5c7196788dc6",
"title": "Android_robot.png"
}
}
Can someone please advice how to get base64 image payload instead of link?
Check to see if you have this handler, most likely you did not change the name of the resource posts to your:
const addUploadCapabilities = requestHandler => (type, resource, params) => {
if (type === 'UPDATE' && resource === 'posts') {
Create your custom dataProvider to convert picture to base64
import restServerProvider from 'ra-data-json-server';
const servicesHost = 'http://localhost:8080/api';
const dataProvider = restServerProvider(servicesHost);
const myDataProfider = {
...dataProvider,
create: (resource, params) => {
if (resource !== 'your-route' || !params.data.pictures) {
// fallback to the default implementation
return dataProvider.create(resource, params);
}
const myFile = params.data.pictures;
if ( !myFile.rawFile instanceof File ){
return Promise.reject('Error: Not a file...'); // Didn't test this...
}
return Promise.resolve( convertFileToBase64(myFile) )
.then( (picture64) => ({
src: picture64,
title: `${myFile.title}`
}))
.then( transformedMyFile => dataProvider.create(resource, {
...params,
data: {
...params.data,
myFile: transformedMyFile
}
}));
}
};
const convertFileToBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file.rawFile);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
export default myDataProfider;
And get image data at your Server API
exports.create = (req, res) => {
if(req.body.myFile){
var file = req.body.myFile;
var fs = require('fs');
var data = file.src.replace(/^data:image\/\w+;base64,/, "");
var buf = Buffer.from(data, 'base64');
fs.writeFile(`upload/${file.title}`, buf, err => {
if (err) throw err;
console.log('Saved!');
});
}};