Closed jamesfzhang closed 2 years ago
Any news? I am interested by this too :+1:
This might be a good solution: https://github.com/johanneslumpe/react-native-fs
Another approach would be to write downloaded assets to a temp directory and load them from there when available. I've only worked with the Obj C part of the native code, but this approach would work there (I assume it could work on the Java side as well). Then the relevant cleanup logic would have to be added to the component lifecycle, to make sure react-native-video
doesn't start filling up disk space.
Wouldn't it be better to think of this as an implementation detail of the native code and maintain the current API?
I'd be up for implementing this for iOS to start with. My initial proposal is that caching is disabled by default, but requested on a per-clip basis like so:
// Cache video locally (write to temp directory)
<Video source={{uri: "https://foo.com/clip1.mp4", cache: true}} ... />
// Don't cache video
<Video source={{uri: "https://foo.com/clip1.mp4"}} ... />
The cache
option would be ignored for local files, since it's only relevant for files loaded over the network.
The native code would then first look for the asset on disk, loading it from there if found, or otherwise load it remotely as usual.
There are at least a couple of decisions that have to be made around this:
Would be great to get some feedback on this. I'll probably start work on this soon at any rate since I need this for an app I'm working on, so the sooner the better.
Sounds like a good approach!
Not sure how I feel about this, but how about being able to configure the params like this?
// Use smart defaults (30MB cache size & purge in 10 minute )
<Video source={{uri: "https://foo.com/clip1.mp4", cache: true } ... />
// Configure cache to 50MB & purge in 1 hour
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 50, expiresIn: 3600 }} ... />
@jamesfzhang The thing with that approach is that these parameters should probably be set globally for the app. For example, if one writes something like this:
<View>
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 50, expiresIn: 3600 }} ... />
<Video source={{uri: "https://foo.com/clip1.mp4", cache: { size: 40, expiresIn: 4800 }} ... />
</View>
Which set of cache settings should be used for caching https://foo.com/clip1.mp4
? This could work fine if users are instructed to always use the same cache settings for the same clip/s, but it feels a bit hacky — I think it would be better to globally configure this somewhere so that it applies to all instances of Video
, but I'm not sure what's the best way to go about that. Modifying the class directly?
I was under the impression that the configurations would be set per video, as opposed to global. I imagine there are some videos you want to cache and others you don't.
But I guess just using global configs would be fine too? Maybe you'd need to add a VideoConfiguration class?
They could be set per video, but it's not clear which settings should take precedence when there are several Video
instances mounted using the same clip, which is OK (if we can assume that users stick to providing the same config via props
to each instance) but not ideal. Otherwise there's no way to know e.g. whether https://foo.com/clip1.mp4
should be invalidated in 3600 seconds or 4800 seconds above.
Something like this would work (maybe not particularly pretty, but then again it only has to be written once):
// require this file instead of require('react-native-video') where Video is to be used
var Video = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 50, expiresIn: 3600, key: 'settings'} };
}
module.exports = Video;
Then the cache settings would be applied globally through the app, and only once. This could also be done several times with different settings, if the user of the library is confident that there won't be conflicts (i.e. two Video
components caching the same clip with differing settings). E.g. for two different contexts within the app with different kinds of clips, with different caching needs:
// LongClipPlayer.js
var LongVideo = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 50, expiresIn: 3600, key: 'longVideoCache'} };
}
module.exports = LongVideo;
// ShortClipPlayer.js
var ShortVideo = class extends (require('react-native-video').default) {
cacheOptions() { return {size: 10, expiresIn: 1200, key: 'shortVideoCache'} };
}
module.exports = ShortVideo;
Then the implementation would put videos created with key
in a temp folder distinct from those used for other keys, so that the invalidation logic only operates on the appropriate caches.
Thoughts on this?
The second case (multiple configs) may be overkill to start with, we could just go for a single global configuration for starters. The defaults should be fine for most users too, so I don't think this should add to the complexity of using the library, unless more control is needed.
A really easy solution is the one that @cancan101 mentioned, using react-native-fs, but yeah as @thsig mentioned there will huge issue with the usage of the disk space if we are just storing everything without an expire limit.
Otherwise I agree with @thsig , let's keep it simple first, nah?
Here is the code that is doing the thing to cache Image, but it is permanent storage, it is never removed :/ https://github.com/mohebifar/react-native-ximage/blob/master/src/Storage.js#L64-L66
One super easy solution to clean the cache is to make a function which check all cached video of the specific application/folder and which deletes the old videos.
Something like this:
module.exports.cleanCache = function (cacheKey, time) {
const videos = readCache(cacheKey);
videos.forEach((video) => {
// is my video older than `time`?
if (isMyVideoOld(video, time) {
deleteVideo(video);
}
});
}
The function will be accessible for the developer and we don't have to care if he/she is going to use it. By default, we can just disable the cache and if the developer wants to use it, he should be aware that he has to use this function to clean it.
Maybe it would be better to separate the caching implementation from react-native-video
, but have an API for react-native-video
to receive pre-loaded AVAsset
-s (and the equivalent for Android) under the covers. It seems to me that this library should primarily concern itself with playback and offload other concerns to other libraries where possible.
What about something like this?
<Video
source={{uri: "https://foo.com/clip1.mp4"}}
cacheName='StevesCache' />
When receiving new sources, the native code would publish a notification to the channel named cacheName
(e.g. via the NSNotificationCenter
system in iOS, and the Java equivalent on Android), containing the source uri/s which would then be received by the StevesCache
native module class (or whatever the class is named - it's decoupled from the channel name provided).
The cache module implementation would then publish a message to the ReactNativeVideo
channel, where the payload would be an array whose elements are either AVAsset*
(or the Java equivalent) or nil
, depending on whether the requested URI was found by the cache implementation's cache (in the same order as the source array sent to it in the previous step).
I'm suggesting an array assuming that multiple-video playback will be added to the library at some point (whether my branch is merged or not, this is probably on the roadmap anyway), so that would pre-empt having to change the API later, possibly after other libraries start depending on it.
Then react-native-video
would treat the cached assets as fully buffered and proceed from there, buffering non-cached assets as usual.
Thoughts, @brentvatne? We might need functionality like this soon, so I'd probably be up for writing the initial iOS implementation. I'm also very open to alternate implementation ideas.
I have a feeling there may be a better way – proxying requests to server. One could set himself as AVAssetResourceLoader
and then check in his own cache if such video has been cached or else load from URL. This approach has been described:
Based on uri
we could initialize under the hood one cache for every custom protocol, eg.:
<Video
source={{uri: "stevesCache://foo.com/clip1.mp4"}}
/>
...
<Video
source={{uri: "globalCache://foo.com/clip1.mp4"}}
/>
Maybe we could use uri
as stevesCache://https://foo.com...
to be able to specify target request, I don't know how NSURL
would handle such URL.
Customizing cache capacity would require calls to some kind of Cache.setCacheCapacity('stevesCache', 40 * 1024 * 1024)
Hi, all:
I want to know when to publish this function? It waste a lot of flow now.
Any updates here? @thsig, definitely think you should take over the repo :)
good point @getnashty! @thsig I have added you as a collaborator on this repo. let me know your npm username and I can give you npm publish access, brentvatne at gmail dot com
This hasn't been touched for a while, but since iOS natively supports offline caching now it's worth reviving: https://developer.apple.com/reference/avfoundation/avassetdownloadurlsession/1650938-makeassetdownloadtask
Video explaining the process. Starts at about 16:20: https://developer.apple.com/videos/play/wwdc2016/504/
Looks like this could all be handled behind the scenes in this package, and all the user has to decide is if the video should be cached and provide it the key name + download location that would be needed to look the task up again.
From the video:
He points out that the video can be played at ANY time during the download process, and it's smart enough to use whats been downloaded already and continue downloading to the cache as the video plays.
Sidenote: A sibling package to this that only does the downloading could be helpful as well. Would be used to load videos even without a react-native-video on the screen. (app is in background, preload video, etc..)
@duhseekoh That's fantastic, thanks for posting that! I should pay more attention to updates.
I've been working on a native iOS app with Swift (which I started a few years ago), and now I'm going rewrite it in React Native. I have been wanting to cache downloaded videos for a very long time, but it was going to be a ton of work. But it looks like AVAssetDownloadTask changes everything, and video caching should be pretty easy to implement now. (Although I'm nervous about Java and Windows.)
I'm going to need this for my app, so I would like to contribute caching for iOS. I might be able to figure out Android, too. Worst case would be that caching only works on iOS and is just ignored on other platforms.
Maybe it would be better to separate the caching implementation from
react-native-video
@thsig - I'm not sure about that, I think most developers would love to pass cache={true}
, and just have it use some sane defaults. E.g. A LRU cache with a max size of 100MB would be great for my app (lots of short videos), and I think it would probably work well for most video sharing apps. And probably a default TTL of 24 hours, with a customizable cacheTTL
.
In the case where you have several Video instances playing the same video, then the last one would have precedence, and we could just show a yellow box warning during development.
@duhseekoh From the looks of it AVAssetDownloadTask requires HLS, right? So you can't use it for any arbitrary video. That seems different than what this issue is about.
any updates on this?
if we use react-native-fs approach,, does it mean the video has to be downloaded fully, before react-native-video can access it? Please correct me if I'm wrong,, but what I want to happen is while the video is playing (and at the same time downloading) in the backgroud, the video file is cache somewhere.
waiting publish this function ...
Is anyone working on this feature?
Would also love to have caching in react-native-video
+1
+1
+1
+1
等待更新
+1
keep follwing...坐等更新
+1
+1
+1
缓存功能还没有实现吗?苦等半年了~~
+1
+1
My app's video resources are all online, hence viewable by using the form: {uri: 'https:...'} But now I would like to offer the user the option to download videos for offline viewing. Yet this appears to be incompatible with RN's way of handling resources of this kind - or with react-native-video's current support for addressing locally stored video content. This is kind of related to the question of caching. Does anyone have any suggestions?
For those interested I started implementing caching for ios here: https://github.com/n1ru4l/react-native-video/tree/implement-ios-caching
🙌🏽
On Thu, Mar 1, 2018 at 2:45 PM, Laurin Quast notifications@github.com wrote:
For those interested I started implementing caching for ios here: https://github.com/n1ru4l/react-native-video/tree/implement-ios-caching
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/react-native-community/react-native-video/issues/99#issuecomment-369757434, or mute the thread https://github.com/notifications/unsubscribe-auth/AKEVzci2WCGkU7HjROi_Ccnp8I-UScvqks5taHoAgaJpZM4GG5sc .
-- Best Regards, Freddie Cabrera
Hey guys, any update on this? Thanks in advance!
@hkdahal You could help out by testing my implementation. The instructions are listed in the Pull Request (#955).
Hello. Did anyone solve the problem of caching video?
@DavitVosk I ended up using this library: https://github.com/kfiroo/react-native-cached-image with that PR: https://github.com/kfiroo/react-native-cached-image/pull/110/commits/31ad016afabcbe42e3c1eb058a0d9a57b0fb615f which allows you to cache all file extensions, instead of only images.
@rnowm Thanks for an answer. Can you please give me an example how you use that package with react-native-video?
@DavitVosk You could use my fork (https://github.com/react-native-community/react-native-video/pull/955) published to npm as @n1ru4l/react-native-video
. Sadly nobody is giving me feedback so it seems like this will not be merged any time soon.
I am using my fork in a production application, so I can ensure you it works. However it is not up to date with the upstream changes, I will try to rebase/merge master soon.
If you try my fork feel free to provide feedback and possible enhancements 😊
@n1ru4l thanks for an answer. So what does it differ from the real rn-video package?
@DavitVosk You can read the differences in the pull request #955
@n1ru4l, it looks a great solution if it works))). Now trying your package but receive several errors like no such file or directory: .../node_modules/react-native-video/ios/RCTVideoPlayerViewController.m
, .../node_modules/react-native-video/ios/RCTVideo.m
, etc... Can you please help me to understand what was the problem. So I had already installed rev-video package, so I unlinked it and now linking your package manually
ok I see that react-native-video
folder is not directly situated in node/modules
- it is located in n1ru4l
folder in node/modules
. And that was giving the above-mentioned errors. Its route should be changed... I copied the all react-native-video
folder directly into node/modules
and the errors went away. But now I get the following error: SPTPersistentCache/SPTPersistentCache.h file not found
How do you cache a video that's downloaded over the network?