John Tiggernaught
John Tiggernaught

Reputation: 788

Playing a video loaded via cdvfile:// in Cordova

Updated Info

I've found that you can't use cdvfile:// when using cordova-ios 6.1.0 because of how WKWebView works. You now need to use window.WkWebView.convertFilePath([your file path]); to convert your path to something WKWebView.

I'm now using the toURL() method found when getting a FileEntry from the cordova-plugin-file plugin and feeding that value into window.WkWebView.convertFilePath.

I'm still getting a similar error, but I think I'm getting closer to cracking this egg:

Refused to load unsafe:app://localhost/_app_file_/var/mobile/Containers/Data/Application/{ID}/Library/NoCloud/videos/recording.MOV because it does not appear in the media-src directive of the Content Security Policy.

I added app: to the CSP and added <access origin=app://* /> to config.xml.

Does anyone know how I can get rid of the error and get the video playing within the <video> element?

Original Post

When trying to play a cdvfile:// file in a <video> element I get the following error, even though I've added, what I think, is the correct properties to my Content Security Policy (found below):

Refused to load unsafe:cdvfile://localhost/library-nosync/videos/recording.MOV because it does not appear in the media-src directive of the Content Security Policy.

What is required to get a locally stored video file working in a basic <video> element using Angular + Cordova?

Additional Information

I'm using Angular 9.0.7, cordova 10.0.0 and cordova-ios 6.1.0 to build an app that plays video recordings. Recordings are created using cordova-plugin-media-capture 3.0.3 and saved to the local device using cordova-plugin-file 6.0.2.

I've added the following to my config.xml:

<access origin="cdvfile://*" />
<preference name="scheme" value="app" />
<preference name="hostname" value="localhost" />

I'm also using the most open/insecure Content Security Policy I could find, with cdvfile: added to default-src, connect-src and media-src:

<meta http-equiv="Content-Security-Policy" content="
    default-src * data: blob: filesystem: about: ws: wss: gap: cdvfile: 'unsafe-inline' 'unsafe-eval'; 
    script-src * filesystem: gap: cdvfile: data: blob: 'unsafe-inline' 'unsafe-eval'; 
    connect-src * filesystem: gap: cdvfile: data: blob: 'unsafe-inline'; 
    img-src * filesystem: gap: cdvfile: data: blob: 'unsafe-inline'; 
    frame-src * data: blob: ; 
    style-src * data: blob: 'unsafe-inline';
    font-src * data: blob: 'unsafe-inline';
    media-src * filesystem: gap: cdvfile: data: blob: mediastream: ;
">

In the documentation for the cordova-plugin-file plugin (link) they provide an example Content Security Policy, but it doesn't appear to be valid:

<meta http-equiv="Content-Security-Policy" content="
    default-src 'self' data: gap:cdvfile:https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline';
    media-src *
">

When using the above, I get the following error on load:

The source list for Content Security Policy directive 'default-src' contains an invalid source: 'gap:cdvfile:https://ssl.gstatic.com'. It will be ignored.

The only way to remove the error is to add a space betweem gap:, cdvfile: and https://ssl.gstatic.com. I ended up removing https://ssl.gstatic.com to make the CSP as generic and "open" as possible.

Even More Information

I can load the MOV file and validate that is exists using the following:

window.resolveLocalFileSystemURL([PATH TO FILE ENTRY], entry => {

    entry.getMetadata(metadata => {
        console.log(metadata); // If metadata.size exists and isn't 0, I believe I have the file
    }, err => {
        console.error(err);
    });

});

Once I have the file (variable named entry in this example) I use the following in my component to get the filepath:

let videoSrc: string = entry.toInternalURL();

This get's me the cdvfile:// path that I then use as the source to play in my video element:

<video *ngIf="videoSrc" id="video" width="640" height="360" preload controls>
    <source [src]="videoSrc" type="video/mp4" />
</video>

... So, yeah. That's my life story... Can anyone shed some light as to what's required to allow me to play local cdvfile files in a standard <video> element?

Upvotes: 0

Views: 1865

Answers (1)

Lindsay-Needs-Sleep
Lindsay-Needs-Sleep

Reputation: 1473

I believe you need to add an explicit permission for app: in the <meta http-equiv="Content-Security-Policy".

        <!-- Enable all requests, inline styles, and eval() -->
        <meta http-equiv="Content-Security-Policy" content="
        default-src * app: gap: data: cdvfile: android-webview-video-poster: file:; 
        style-src 'self' 'unsafe-inline'; 
        script-src * 'unsafe-inline'">

(For some reason default-src * wasn't enough. I had to add default-src * app: to make it work on http:// urls.)
It also appears to work if you just don't have a <meta http-equiv="Content-Security-Policy" at all.


My life story since it took me a long time to figure out why it wouldn't be working for you:
I can confirm that this should work.
Based on your "Updated Info"...

I am doing exactly what you are with cdvfile > nativeUrl > schemeUrl

  • The schemeUrl works for me.

When I use the schemeUrl in an <img> and <video> src tag on:

  • a page with a schemeUrl (app://localhost/...):
    • No problems.
  • a page with an https://... url:
    • A warning: "The page at https://... was allowed to display insecure content from app://localhost/..."
    • but it works.
  • a page with http://
    • I get the warning you received.

Notes / Other possibilities thoughts:
Your <access origin=app://* />
Should probably be <access origin="app://*" />
But I don't think this is your problem as I don't have an <access origin/> tag at all in my config.xml.

You can try adding:
<allow-navigation hap-rule="yes" href="app://localhost/*" />
to your config.xml (same place where you would have <access origin=.../>) But I also just tried my app without this and it was fine.

You could also try upgrading to [email protected]

Upvotes: 0

Related Questions