I'm a novice when it comes to the development of apps. I made a simple audio player app for android and iOS and build it with phonegap build.
The app uses the <audio> html tag to load mp3 files that are located on a remote server like so:
<div class="mobileui-music-cover-controls">
<audio ontimeupdate="udpateProgress()" id="player">
<source src="http://www.myserver.com/audiofile.mp3" type="audio/mpeg">
</audio>
<a id="playKnop" href="JavaScript:playMusic()" class="play"><i class="ion-ios-play"></i></a>
<a id="pauseKnop" href="JavaScript:pauseMusic()" class="play"><i class="ion-ios-pause"></i></a>
</div>
obviously, I changed the scr for this example.
It works fine as long as you have a stable internet connection but a few people are experiencing connection drops etc.
I would like to make the files offline available on user request. So it has to be optional (by clicking a button or something like that).
The app should detect if the file is present on the device and if so choose the local file over the remote file.
In short, I have 2 questions.
How can I download a specific file on user request?
How can I check whether the file is there or not and play the right file?
I can't seem to figure out how to do this I'm working on it for the last couple of days but I honestly have no clue on how to achieve this. Any help would be greatly appreciated.
As you said you need to download the file to your device. Since your using cordova you could use fileTransfer to download the audio. This is very straight forward (this is the offical example code):
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("download error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
The next time you want to play the audio you need to check if there is a local version. Either you check your device if that file exists (there are many ways, e.g. Cordova check if file in url exists) or you make use of something like localStorage which basically is a simple lil database.
// more or less pseudo code!!!
// callback from fileTransfer when your file was downloaded
function downloadSuccess(entry) {
// save it to localStorage
// key: the remote URL, value: the local URL
localStorage.setItem(remoteURL, entry.toURL());
}
...
...
// the check if there is a cached file
var remoteSrc = "http://www.myserver.com/audiofile.mp3";
var localSrc = localStorage.getItem(remoteSrc);
if(localSrc === null) {
// when there is NO cached version, use remote
audioElement.src = remoteSrc;
} else {
// when there IS a cached version, use local
audioElement.src = localSrc;
}
I hope this helps and gives you an idea how to accomplish simple caching yourself :)
Related
I'm using cordova-plugin-file to read my mp3 file with the readAsArrayBuffer method. It works perfect with a file less than 20mb, but with a larger file it causes the app to crash with this error. (I'm using crosswalk browser)
E/chromium( 3330): [ERROR:simple_index_file.cc(223)] Failed to write the temporary index file
E/chromium( 3330): [ERROR:runtime_javascript_dialog_manager.cc(69)] Not implemented reached in virtual void xwalk::RuntimeJavaScriptDialogManager::ResetDialogState(content::WebContents*)
I'm so confused with what the problem is. Does the problem come from xwalk or cordova-plugin-file?
Please help me because this plugin can only read file smaller than 20mb size.
I found a solution for this bug.
I think Cordova-plugin-file can't send large amount of data from native to javascript. So I try to research from Crosswalk Browser API and very happy to see that they support File API. It can get access directly to Android file system through virtual root like : EXTERNAL, CACHEDIR, DOWNLOADS, ...
Here is the trick to read any big file with Crosswalk:
function readFileAsArrayBuffer(storage, path, file) {
xwalk.experimental.native_file_system.requestNativeFileSystem(storage,
function (fs) {
fs.root.getFile(storage + "/" + path + file, {create: false}, function (entry) {
entry.file(function (file) {
reader = new FileReader();
reader.onloadend = function (data) {
//Data after read.
};
reader.readAsArrayBuffer(file);
},
},
function (e) {
console.error("2-" + JSON.stringify(e))
});
},
function (e) {
console.error("3-" + JSON.stringify(e));
});
}
//test
readFileAsArrayBuffer("EXTERNAL", "downloads/folder/", "file.mp3");
I am able to download an image if I specify the path directly with
file:///storage/sdcard0/
How can I save an image to my one of the folders in my app ? I tried this approach to set app path but it doesn't work for me.
This is what I am using so far and it works if you want to save and image to sdcard:
var fileName = "myImage.png";
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://my.url.to.image/myImage.png");
var filePath = "file:///storage/sdcard0/uploads/myImage.png";
fileTransfer.download(
uri,
filePath,
function(entry) {
alert("download complete: " + entry.fullPath);
console.log("download complete: " + entry.fullPath);
},
function(error) {
alert("download error source/target/code:\n" + error.source +" \n||| "+error.target+" \n||| "+error.code);
console.log("download error source/target/code " + error.source+" / "+error.target+" / "+error.code);
}
);
If I use this function:
function getPhoneGapPath() {
'use strict';
var path = window.location.pathname;
var phoneGapPath = path.substring(0, path.lastIndexOf('/') + 1);
return phoneGapPath;
}
I get /android_asset/www/. How would I get the correct path to my app?
The app is using AngularJS and is not bootstrapped in onDeviceReady (developer before me made it that way and now changing it to something like this is beyond me (tried but didn't work)).
Another question I can refer to was asked here.
I also tried this, this, this and this but none worked for me. I get "" for fullPath and recently I managed to get path printed with .toURL() and it was "cdvfile://localhost/persistent". The above download code now also works if I use
filePath = "cdvfile://localhost/persistent/MyAppID/uploads/myImage.png";
but this creates folder /MyAppID/uploads in /storage/sdcard0 which is bad again. My app needs to get to images with
<img src="uploads/myImage.png" alt="myimg"/>
Another useful link is here but it offers no help as to how to write to pre-created folder of your own app.
EDIT: As far as I've learned you cannot write within your own app(read only). That's why I tried to reference images from sdcard with
<img src="file:///storage/sdcard0/Android/data/com.app.my/cache/myImage.png" alt="myimg"/>
and this works! :) Unfortunately this is hardcoded and not good since other phones might have a different persistent location.
If I try
<img src="cdvfile://localhost/persistent/Android/data/com.app.my/cache/myImage.png" alt="myimg"/>
this does not reference the picture :/ (it does download it on storage/sdcard0/Android/data/com.app.my/cache/ though)
I would use the folder alias as defined in the plugin docs here (https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md). Specifically cordova.file.dataDirectory. Note that this is not, afaik, under the www of your original project, but it seems to be the preferred place to store downloads. Once saved there you can resolve that to a URL that you could use to load via AJAX, or in an img tag if you are downloading graphics.
As far as I learned you cannot write to your own app (change items inside your app - /www folder on Android).
My solution was to save images to PERSISTENT storage (sdcard usually).
You can get the path of your app's persistent storage with these steps:
function onDeviceReady() {
console.log('Device Platform: ' + device.platform); // returns 'Android' or 'iOS' for instance
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
cacheFolderSubPath = "Android/data/com.id.myApp/cache/"; // this is app's cache folder that is removed when you delete your app! (I don't know what this path should be for iOS devices)
}
Then inside gotFS success callback
function gotFS(fileSystem) {
var nturl = fileSystem.root.toNativeURL(); // cdvfile://localhost/persistent/
window.resolveLocalFileSystemURL(nturl+cacheFolderSubPath, onResolveSuccess, onResolveFail);
}
and in onResolveSuccess
function onResolveSuccess(fileEntry){
appCachePath = fileEntry.toNativeURL()+"/"; // file:///storage/sdcard0/Android/data/com.id.myApp/cache/ in my case but some other Androids have storage/emulated/0 or something like that ...
continueCustomExecution();
}
and now in continueCustomExecution() you can run your program and do whatever it is you do ... and in this case download images into appCachePath that we got earlier. You can now successfully reference images in src tags with our appCachePath+yourImageName.
Unfortunately I still don't know how to download an image successfully on iOS. I get an error code 1 with FileTransfer plugin ... (saving to file:///var/mobile/Applications/my.app.id/Documents/). Probably should save somewhere else but this is the path I get when requesting PERSISTENT FileSystem.
Cheers
I have tried for days now to play a local video file on my galaxy tab 4.2 through a cordova 3.4 app.
When i use an absolute http url the video plays just fine.
Here is what i have tried:
I put the video file in the assets/res/raw folder as suggested here: Loading video failed on html5 phonegap
RESULT: After i click on play -> spinning loading icon no video
Video in the www folder:
Result: Same as #1
<video id="myvideo" controls="controls" width="400">
<source src="file:///android_asset/www/gruppenruf.mp4" />
</video>
Result: Same as #1
I set all the permissions of the folders to 777
Then i tried with the https://github.com/jaeger25/Html5Video plugin
After installing the plugin all i get is:
03-06 18:27:06.953: E/Web Console(22530): Uncaught TypeError: Cannot read property 'html5Video' of undefined:37
All i am trying to do is play a local video file on android. Is this really that complicated?
any help would be appreciated.
Take a look at this post.
The File plugin (at least <=v1.3.1) has a bug for android devices. Also, I am not sure if jaeger25/Html5Video plugin is still working with cordova 3.6.x.
A working approach is to programmatically copy your video files from www/gruppenruf.mp4 to a place accessible for playback by the app during runtime. You may use file:///data/data/com.example.MyPackage/files/gruppenruf.mp4 for that. The FileTransfer cordova plugin will take care of this.
var myFilename = "gruppenruf.mp4";
var myUrl = cordova.file.applicationDirectory + "www/" + myFilename;
var fileTransfer = new FileTransfer();
var filePath = cordova.file.dataDirectory + myFilename;
fileTransfer.download(encodeURI(myUrl), filePath, (function(entry) {
/*
res = "download complete:\n"
res += "fullPath: " + entry.fullPath + "\n"
res += "localURL: " + entry.localURL + "\n"
alert(res += "nativeURL: " + entry.nativeURL + "\n")
*/
var vid = document.getElementById("myvideo");
vid.src = entry.nativeURL;
//vid.loop = true;
}), (function(error) {
alert("Video download error: source " + error.source);
alert("Video download error: target " + error.target);
}), true, {
headers: {
Authorization: "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
});
If it does not play you may want to link a click event listener to it or start the video with vid.play();.
If the mentioned options are not working try:
src="android.resource://com.example.hello/raw/Videofile" (leave the extension ".mp4" what so ever)
place your video files in "..\platforms\android\res\raw"
note that you don't use the extension of the video file in the src.
I've struggled a lot with this issue, but this worked for me =]
As I spend some time with a similar issue, I feel its worth sharing:
If you want to display video fullscreen: dont use html tag as there is a media plugin for that. If you dont need to play offline: the video src pointing to a web server will work just fine.
If you need to play offline and the video is part of your app package, have a look to https://github.com/jaeger25/Html5Video (it help to automatically copy the video files in a public location)
If you need to play offline videos dynamically downloaded: (my case)
Use fileTransfer plugin
Save file in cordova.file.externalDataDirectory as its public
In some case you may need to force the file to be public
you can do that by adding 'file.setReadable(true, false);' to FileTransfer.java line 820
Cheers
Your code stopped working in Cordova 3.4.0 because they changed the file path structure. Instead of 'file:///' you have to use 'cdvfile://'.
Remember to update the File Plugin as well.
I have simple video playing apps both set up by PhoneGap and Chrome Apps CLI's (Both use Cordova), they contain a bunch of short educational videos and are needed as both website and application on Android/iOS for offline usage.
I have found, so far, that the total size of the Chrome Apps bundled file can't exceed 10mb and the PhoneGap Build can't exceed 40mb - so both will need to download and store files locally for later use.
The videos will need to open and play from within the WebView browser - hotspots trigger JS that change the HTML5 video src. (AppCache and other HTML5 storage are out the question for mobile devices, they never seem to be able to reach triple digit storage space)
Has anyone had luck with a certain Cordova/PhoneGap/Chrome App API that can store files locally to achieve this spec?
Any advice/help/pointing in right direction appreciated!
You can do this in Cordova apps (and very soon in Chrome Cordova apps). You'll need the most recent versions of the File (1.0.1) and FileTransfer (0.4.2) plugins.
With those, you can use FileTransfer.download() to download the video, and you can use File to access the file and create a <video> tag to play the video.
You'll want to use the .toNativeURL() method on the file entries before you play them. Recent versions of the File plugin use a custom URL scheme for files, which is unfortunately not compatible with the HTML <video> tag.
This is the test code that I use to test the interaction of these methods:
var filename = "small.mp4";
var videoURL = "http://techslides.com/demos/sample-videos/small.mp4";
requestFileSystem(PERSISTENT, 0, function(fileSystem) {
var ft = new FileTransfer();
ft.download(videoURL, fileSystem.root.toURL() + "/" + filename, function(entry) {
var videoElement = document.createElement('video');
videoElement.controls = 'controls';
videoElement.src = entry.toNativeURL();
document.videoElementById("output").appendChild(imgElement);
});
});
Update
With the latest version of the File plugin (1.1.0), you no longer need to use .toNativeURL() to obtain a URL that you can use as a src attribute for a video. The standard .toURL() method will return such a URL.
Here is the code to download file using phonegap filetransfer
function downloadFile(){
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function onFileSystemSuccess(fileSystem) {
fileSystem.root.getFile(
"test.html", {create: true, exclusive: false},
function gotFileEntry(fileEntry){
var Path = fileEntry.fullPath.replace("test.html","");
var fileTransfer = new FileTransfer();
fileEntry.remove();
fileTransfer.download(
yourserverurl,
Path + "yourfilename+extension",
function(theFile) {
window.localStorage.setItem("FilePath", theFile.toURL());
console.log(theFile.toURL());
},
function(error) {
console.log("upload error code: " + error.code);
}
);
},
fail);
},
fail);
}
function fail(error) {
console.log(error.target.error.code);
}
You can store the fileURL in localstorage for further usuage
I am unable to access the filesystem using steroid.js. I have used the exact same code within phonegap and do not have the issue. here is my code and I do not appear to get any errors in my console.
var captureSuccess = function(mediaFiles) {
mediaFiles[0].getFormatData(function(data) {
if(data.duration > 10) {
window.resolveLocalFileSystemURI(mediaFiles[0].fullPath, FileRemoveSuccess, function() {
navigator.notification.alert('Error: Unable to access file system')
});
}
else{
$('#video-content').append('<video width="320" height="240" controls><source src="' + mediaFiles[0].fullPath + '" type="video/mp4"></video><br><span>' + mediaFiles[0].fullPath + '</span>');
}
});
};
// capture error callback
var captureError = function(error) {
navigator.notification.alert('Error code: ' + error.code, null, 'Capture Error');
};
// start video capture
function RecordVideo(){
navigator.device.capture.captureVideo(captureSuccess, captureError, {duration:10});
}
I have also tried to access an image by putting in the path manually or a video and I have the same issues. Is there something I am missing?
If you're serving your app via localhost (e.g. steroids.config.location = http://localhost/index.html, then built-in WebKit security will block assets from file:///, since it's in a different domain. Is that the case? We're working on a solution for all CORS issues, but see http://guides.appgyver.com/steroids/guides/steroids-js/app-structure-on-device/ for information on how a Steroids app is structured on the device (and thus how you can access files via localhost).
It turns out that not only your index.html file needs to be served as a file but so do any other webviews you create using the new steroids.views.WebView command. Once I corrected this, the issue was resolved