I have an audio file stored on the server and clients can request these audio files via a REST API. I observed the data requested by the client and I came to the conclusion whenever I play the audio again (repeat), then a new request goes to the server for that specific file. Is there a way to prevent this or cache the data locally?
HTML
<audio controls="controls" autoplay style="width: 100%">
<source src="https://localhost:8080/api/file" type="audio/mpeg">
</audio>
Javascript (Node.js in combination with Express.js)
router.get('/file', (req, res) => {
let audioFile = path.join(__dirname, 'files/audio-book.mp3');
res.sendFile(audioFile);
})
EDIT
I have a local file and a file sent by the REST API. The local file request has HTTP code 304: Not modified and the file from the REST API request is 206: Partial content. I already added max-age and removed last-modified, but no result.
Headers:
Accept-Ranges -> Bytes
Cache-Control -> public, max-age=3600
Connection -> keep-alive
Content-Length -> 7056954
Content-Type -> audio/mpeg
Date -> Sun, 17 Jan 2016 10:16:48 GMT
Etag -> W/"6bae3a-15195a7f2aa"
X-Powered-By → Express
Situation
What happened was that the browser wouldn't cache the file in any way, shape or form. This let to tremendous amounts of traffic.
Solution
I found this article on StackOverflow about requesting files in blobs and letting an audio- or video element play it. Now I XHR the file from the server and store it in a BLOB and when I replay the audio, it retrieves the data from cache and it can work offline now.
Related
I have pulled down an example application running on nodejs. The application is doing a "get" on a local JSON file for its base configuration and capable to rendering changes on the fly (Netflix Vizceral example app).
I have added a simple setInterval so the client grabs the file every 30 seconds. The problem is that the client keeps getting the original file from when the server side app was started instead of what is currently there.
Original:
request.get('sample_data.json')
.set('Accept', 'application/json')
I have tried variations of:
request.get("sample_data.json?ts="+now,{cache:false})
.set('Cache-Control', 'private, no-cache, no-store, must-revalidate')
Not sure I am missing something in the javascript or server side node configuration.
I am accessing Azure Media Rest API using javascript (angular js), I followed the Microsoft documentation(https://learn.microsoft.com/en-us/azure/media-services/media-services-rest-get-started) through which I was able to generate the token, connect to media services, create the access token and the locator. But after this step, I am facing a problem in uploading a file into a blob storage container.
Is there any way to perform uploading of a file via client side using Angular js or javascript?
while making a request:-
PUT https:/storageaccountname.blob.core.windows.net/assetcontainer/filename?sas token HTTP/1.1
Host: storageaccount.blob.core.windows.net
Connection: keep-alive
Content-Length: 223
x-ms-blob-type: BlockBlob
Origin: http://localhost
Authorization : SharedKey accountnamw: access key
Content-Type: video/mp4
Accept: application/json,
x-ms-version: 2011-08-18
This gives a response :-
HTTP/1.1 400 Authentication information is not given in the correct format. Check the value of Authorization header.
Content-Length: 297
Content-Type: application/xml
x-ms-request-id: e6b8eeaa-0001-003e-4403-6958b9000000
Access-Control-Allow-Origin:localhost
Access-Control-Allow-Credentials: true
Date: Sat, 07 Jan 2017 16:34:38 GMT
On trying to remove authorization header and date header from the request, the response we receive is a 404 error: resource does not exist. I am looking for a proper solution to upload file to the blob storage container, kindly help me in resolving this issue .
Thank you..
Since uploading is not something that is handled by the Media Services API itself, you would need to look for examples of using the Azure Storage APIs to upload into a Block Blob via a SAS URL.
Since you have a writable Locator (SAS URL) already, you now need to write to the block blob.
I've seen some examples of doing this out there on the web from client side Angular code. For example - https://blogs.msdn.microsoft.com/kaevans/2015/12/18/securely-upload-to-azure-storage-with-angular/
I'll check to see if we have any more examples
Currently, there is not an existing Azure storage SDK for javascript in client side using. So we can manually generate SAS token URLs fro uploading blobs from client applications.
You can refer to https://learn.microsoft.com/en-us/azure/storage/storage-dotnet-shared-access-signature-part-1 for the detailed guidance.
Also, you can refer this thread Authorization of Azure Storage service REST API as we discussed how to generate SAS token in javascript on client side previous.
I have a mostly static HTML website served from CDN (plus a bit of AJAX to the server), and do want user's browsers to cache everything, until I update any files and then I want the user's browsers to get the new version.
How do I do achieve this please, for all types of static files on my site (HTML, JS, CSS, images etc.)? (settings in HTML or elsewhere). Obviously I can tell the CDN to expire it's cache, so it's the client side I'm thinking of.
Thanks.
One way to achieve this is to make use of the HTTP Last-Modified or ETag headers. In the HTTP headers of the served file, the server will send either the date when the page was last modified (in the Last-Modified header), or a random ID representing the current state of the page (ETag), or both:
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Fri, 18 Dec 2015 08:24:52 GMT
ETag: "208f11-52727df9c7751"
Cache-Control: must-revalidate
If the header Cache-Control is set to must-revalidate, it causes the browser to cache the page along with the Last-Modified and ETag headers it received with it. On the next request, it will send them as If-Modified-Since and If-None-Match:
GET / HTTP/1.1
Host: example.com
If-None-Match: "208f11-52727df9c7751"
If-Modified-Since: Fri, 18 Dec 2015 08:24:52 GMT
If the current ETag of the page matches the one that comes from the browser, or if the page hasn’t been modified since the date that was sent by the browser, instead of sending the page, the server will send a Not Modified header with an empty body:
HTTP/1.1 304 Not Modified
Note that only one of the two mechanisms (ETag or Last-Modified) is required, they both work on their own.
The disadvantage of this is that a request has to be sent anyways, so the performance benefit will mostly be for pages that contain a lot of data, but particularly on internet connections with high latency, the page will still take a long time to load. (It will for sure reduce your traffic though.)
Apache automatically generates an ETag (using the file’s inode number, modification time, and size) and a Last-Modified header (based on the modification time of the file) for static files. I don’t know about other web-servers, but I assume it will be similar. For dynamic pages, you can set the headers yourself (for example by sending the MD5 sum of the content as ETag).
By default, Apache doesn’t send a Cache-Control header (and the default is Cache-Control: private). This example .htaccess file makes Apache send the header for all .html files:
<FilesMatch "\.html$">
Header set Cache-Control "must-revalidate"
</FilesMatch>
The other mechanism is to make the browser cache the page by sending Cache-Control: public, but to dynamically vary the URL, for example by appending the modification time of the file as a query string (?12345). This is only really possible if your page/file is only linked from within your web application, in which case you can generate the links to it dynamically. For example, in PHP you could do something like this:
<script src="script.js?<?php echo filemtime("script.js"); ?>"></script>
To achieve what you want on the client side, you have to change the url of your static files when you load them in HTML, i.e. change the file name, add a random query string like unicorn.css?p=1234, etc. An easy way to automate this is to use a task runner such as Gulp and have a look at this package gulp-rev.
In short, if you integrate gulp-rev in your Gulp task, it will automatically append a content hash to all the static files piped into the task stream and generate a JSON manifest file which maps the old files to newly renamed files. So a file like unicorn.css will become unicorn-d41d8cd98f.css. You can then write another Gulp task to crawl through your HTML/JS/CSS files and replace all the urls or use this package gulp-rev-replace.
There should be plenty of online tutorial that shows you how to accomplish this. If you use Yeoman, you can check out this static webapp generator I wrote here which contains a Gulp routine for this.
This is what the HTML5 Application Cache does for you. Put all of your static content into the Cache Manifest and it will be cached in the browser until the manifest file is changed. As an added bonus, the static content will be available even if the browser is offline.
The only change to your HTML is in the <head> tag:
<!DOCTYPE HTML>
<html manifest="cache.appcache">
...
</html>
<script>
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.src = 'http://subdomain.domain.org:port/;stream/1';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
audio.crossorigin="anonymous";
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById('audio_box').appendChild(audio);
context = new (window.AudioContext || window.webkitAudioContext)(); // AudioContext object instance // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.crossOrigin = 'anonymous'
source.connect(analyser);
analyser.connect(context.destination);
frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper(){
(requestAnimationFrame || webkitRequestAnimationFrame)(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);//get frequency
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
ctx.fillStyle = '#00CCFF'; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
Audio API gives MediaElementAudioSource outputs zeroes due to CORS access restrictions because I'm trying to play a SHOUTcast URL. I don't know what to do; I have tried all solutions on the internet but nothing worked. Any help will be appreciated.
The URL works perfectly with audio element so its not about the URL; I have even tried something like http://subdomain.domain.org:port/file.mp3. And I found on the internet people using Icecast which is .ogg have same problem. How to fix this?
In my response I will assume the following setup:
Your stream URL is http://stream.radio.com:8000/mount (or http://stream.radio.com:8000/;stream/1 for Shoutcast)
Your paget URL where you place your HTML/JS code URL is http://radio.com/player
To get this working you need:
Set the "Access-Control-Allow-Origin" header of your stream to your domain or *
In javascript, set audio tag crossOrigin property to "anonymous" audio.crossOrigin="anonymous";
Another option it to move you stream URL to the original domain using reverse proxy.
With Icecast you cat set the "Access-Control-Allow-Origin" header using configuration file, just add the following lines to your icecast.xml, I usually add them right after the opening <icecast> tag:
<http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
<header name="Access-Control-Allow-Headers" value="Origin, Accept, X-Requested-With, Content-Type, If-Modified-Since" />
<header name="Access-Control-Allow-Methods" value="GET, OPTIONS, HEAD" />
</http-headers>
Don't forget to restart Icecast after these changes. When your Icecast will be back online you can check the headers with this command:
lynx -head -dump http://stream.radio.com:8000/mount
Response should look something like this:
Server: Icecast 2.4.2
....
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, If
-Modified-Since
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
As you can see, "Access-Control-Allow-Origin: *" header is present.
Shoutcast
Unfortunately, Shoutcast does not allow you to set HTTP headers (.htaccess is not an option too), but we can create a reverse proxy in web server configuration, this will allow you to host the stream from the main domain - radio.com. I will provide proxy configurations for Nginx and Apache.
Nginx
You can add additional headers with "proxy_set_header", but the basic example is:
server {
listen 80;
server_name radio.com;
....
location /stream {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://stream.radio.com:8000/mount;
}
....
}
Apache
Activate Apache proxy modules, and update radio.com virtual host configuration configuration:
<VirtualHost *:80>
ServerName radio.com
....
ProxyPass /stream http://stream.radio.com:8000/mount
</VirtualHost>
Now you can access your stream using http://radio.com/stream URL and the CORS policy will not apply. This solution also brings some additional perks:
you can convert your http Shoutcast/Icecast stream to https, so the browsers will not complain about accessing unsecure content when you will embed your stream to the page hosted with https. (Icecast supports SSL configuration itself)
8000 port will be replaced with port 80, that will allow listeners with 8000 port behind firewall to access your stream.
That is an HTTP header. You would configure your webserver or webapp to send this header. Perhaps in htaccess or PHP. remove the below line
<header name = "Access-Control-Allow-Origin" value = "*" />
SHOUTcast servers do not support CORS. There is nothing you can do to change this if you are going to continue to use SHOUTcast.
First of all, MediaElementAudioSource doesn't have a property named "crossOrigin".
I just find this problem, and mad with the Message:MediaElementAudioSource outputs zeroes due to CORS access restrictions for. But it's just a message, i can still hear the audio.
And I googled lots of this, think this link will be helpful:http://www.codingforums.com/javascript-programming/342454-audio-api-js.html
The createMediaElementSource method should create an object that uses the MediaElementAudioSourceNode interface. Such objects are subject to Cross-Origin Resource Sharing (CORS) restrictions based on the latest draft of the Web Audio API spec. (Note that this restriction doesn't appear to be in the outdated W3C version of the spec.) According to the spec, silence should be played when CORS restrictions block access to a resource, which would explain the "outputs zeroes" message; presumably, zero is equivalent to no sound.
To lift the restriction, the owner of the page at
http://morebassradio.no-ip.org:8214/;stream/1 would need to configure
their server to output an Access-Control-Allow-Origin header with
either a list of domains (including yours) or the * value to lift it
for all domains. Given that this stream appears to already be
unrestricted, public-facing content, maybe you can convince the owners
to output that header. You can test whether the header is being sent
by pressing Ctrl+Shift+Q in Firefox to open the Network panel, loading
the stream through the address bar, and then inspecting the headers
associated with that HTTP request in the Network panel.
Note that they can't use a meta element here since the audio stream
is, obviously, not an HTML document; that technique only works for
HTML and XHTML documents.
(While you're messing with Firefox panels, you may want to make sure
Security errors and warnings are enabled (by clicking the Security
button or its arrow) in the Console panel (Ctrl+Shift+K). I'm not sure
if there's a corresponding CORS message in Firefox like in Chrome, but
there might be. I wasted a bunch of time wondering why a page wasn't
working one day while troubleshooting a similar technology, Content
Security Policy (CSP), only to find that I had the relevant Firefox
messages hidden.)
You shouldn't need to mess with the crossorigin property/attribute
unless you set crossorigin = "use-credentials" (JavaScript) or
crossorigin="use-credentials" (HTML) somewhere, but you probably
didn't do that because that part of the HTML spec isn't finalized yet,
and it would almost certainly cause your content to "break" after
doing so since credentials would be required at that point.
I'm not familiar with the Web Audio API, so I wasn't able to figure
out how to output a MediaElementAudioSourceNode and trigger an error
message for my own troubleshooting. If I use createMediaElementSource
with an HTMLMediaElement (HTMLAudioElement), the result doesn't seem
to be a MediaElementAudioSourceNode based on testing using the
instanceof operator even though the spec says it should be if I'm
reading it right.
Then in my situation, i get the HTTP response Header:
HTTP/1.1 206 Partial Content
Date: Thu, 02 Jun 2016 06:50:43 GMT
Content-Type: audio/mpeg
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Content-Disposition: inline; filename="653ab5685893b4bf.mp3"
Content-Transfer-Encoding: binary
Last-Modified: Mon, 16 May 2016 02:00:05 GMT
Server: nginx
Cache-Control: public, max-age=31536000
ETag: "FpGQqtcf_s2Ce8W_4Mv6ZqSVkVTK"
X-Log: mc.g;IO:2/304
X-Reqid: 71cAAFQgUBiJMVQU
X-Qiniu-Zone: 0
Content-Range: bytes 0-1219327/1219328
Content-Length: 1219328
Age: 1
X-Via: 1.1 xinxiazai211:88 (Cdn Cache Server V2.0), 1.1 hn13:8 (Cdn Cache Server V2.0)
Connection: keep-alive
Note that "Access-Control-Allow-Origin: *", i think this just the right thing, but i still get the message. Hope it help you.
I am trying to have my webpage display a video from my video server. The video server is running on the same host as my webserver, but on a different port.
When I use canvas.toDataURL() to scrape the pixels off the video that is playing on the canvas, I am getting a "Uncaught Error: SECURITY_ERR: DOM Exception 18" on the browser.
I am using Chrome, version 24.
Here is the HTTP header sent with the video page
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: video/x-mp4
Transfer-Encoding: chunked
Date: Mon, 04 Feb 2013 23:28:00 GMT
Server: OizysLight
What am I missing? Shouldn't the "Access-Control-Allow-Origin: *" allow cross origin resource sharing?
You probably need to set the crossOrigin property on the <video> element. You can set it to anonymous if the request for the video resource doesn't need cookies or HTTP auth, or use-credentials otherwise.
Mozilla has a writeup about cross-domain images on a <canvas>, and virtually all of it also applies to videos.
I found the answer. I needed to set the crossOrigin property on my video.
video.crossOrigin = "Anonymous";