I tried to use this snippet from the Soundcloud API:
<script src="http://connect.soundcloud.com/sdk.js">
<script>
SC.initialize({
client_id: 'YOUR_CLIENT_ID'
});
# stream track id 293
SC.stream("/tracks/293", function(sound){
sound.play();
});
</script>
It works in any browsers aside from Safari mobile, both on iPhone and iPad, where the music stream does not play at all.
What am I doing wrong? (I replaced track id and client id with my own details)
Thanks
Here is work around for this issue. What you need to add extra html element.
<div class="player-helper"></div>
JavaScript
SC.stream('/tracks/' + id, {
useHTML5Audio: true,
debugMode: true
}, function(sound) {
let $eventEmitter = $('.player-helper');
$eventEmitter.on('click', function () {
sound.play();
});
$eventEmitter.trigger('click');
$eventEmitter.off('click');
});
In mobile safari the play has the restriction that audio playback has to be triggered by a user action. So you'll have to add some sort of button or link which when clicked will call the sound.play() function.
Related
I'm having a hard time getting the Web Audio API to do what I want. My goal is to eventually trigger voice recognition in a PWA on mobile, but first I decided to do a test case of incrementing a counter whenever play or pause was pressed. I am using the Vue framework.
Here is an abbreviated version of my code along with the GUI is creates on the screen.
<template>
<v-app>
<v-btn #click="activateMediaButton">Start</v-btn>
<div>{{counter}}</div>
<v-btn #click="increment">Increment</v-btn>
</v-app>
</template>
<script>
export default {
name: 'App',
data(){
return {
counter: 0
};
},
methods: {
increment(){
console.log('ssedfr');
this.counter++;
},
activateMediaButton(){
navigator.mediaSession.metadata = new window.MediaMetadata({})
navigator.mediaSession.playbackState = 'playing';
try{
navigator.mediaSession.setActionHandler('play', this.increment)
navigator.mediaSession.setActionHandler('pause',this.increment)
}
catch(error){console.log(error);}
}
}
}
</script>
As you can see, the action handlers are registered when I click the start button on the GUI, and the counter value is supposed to go up whenever I press the media buttons. I have tested this counter using the Increment button element on the GUI and it works fine, but the calls to increment aren't even being fired when the media keys are pressed as confirmed by console.log statements. No errors are thrown when the try block is executed to register the actions.
I am testing this functionality by using the Media keys on my laptop, which otherwise do play and pause media available in any open tabs. I have closed all other media tabs so that nothing is intercepting the keystrokes. I do notice that whenever I press the media button after the actions are registered, the focus goes to the Start button element that triggered the registration in such a way that it looks like a click is being held on it.
I have also tested this on my phone through a Netlify hosted PWA and bluetooth earphones and it doesn't work there either.
To work with Web Media Session API, the page must have to be playing a media file (video or audio).
Here is a working example:
<template>
<div>
<button #click="activateMediaButton">Start</button>
<div>{{ counter }}</div>
<button #click="increment">Increment</button>
<audio
src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
controls
></audio>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
counter: 0,
};
},
mounted() {
navigator.mediaSession.metadata = new window.MediaMetadata({
title: "Unforgettable",
artist: "Nat King Cole",
album: "The Ultimate Collection (Remastered)",
artwork: [
{
src: "https://dummyimage.com/512x512",
sizes: "512x512",
type: "image/png",
},
],
});
navigator.mediaSession.setActionHandler("play", this.increment);
navigator.mediaSession.setActionHandler("pause", this.increment);
},
methods: {
increment() {
this.counter++;
// handle action from here
},
activateMediaButton() {
if (navigator.mediaSession.playbackState === "playing") {
navigator.mediaSession.playbackState = "pause";
document.querySelector("audio").pause();
} else {
navigator.mediaSession.playbackState = "playing";
document.querySelector("audio").play();
}
},
},
};
</script>
To understand how it works try pausing the audio manually from the page and then try using the media keys.
This question already has an answer here:
Facebook ignoring OG image on first share
(1 answer)
Closed 6 years ago.
First of all hi and thanks in advance to anyone who can help with this because I've been going crazy over this for weeks now.
So I've got a website which lists gif taken from my mobile application (which are then stored on AWS and my visitors ( I haven't found a use for me to have users) can share these gifs on facebook using the facebook sdk.
The problem appears when I try sharing an image for the first time
This is what the share dialog shows the first time I click on my sharing button:
http://i.stack.imgur.com/lNVNF.png
and then I close and reclick the same button and now it works:
http://i.stack.imgur.com/YsDUm.png
Now I've been trying to find a way to make this work on the first sharing attempt but to no avail.
I am using meteor in combination with biasport:facebook-sdk and Amazon S3 for the hosting of my files.
Edit here is the code used:
FRONT SIDE
HTML
<div class="facebook share">
<img src="/gallery/fb.png">
</div>
Javascript
Template.*templateName*.events({
'click .facebook': function(e){
e.preventDefault();
e.stopPropagation();
// this is in a modal so I store the data I need
// (events have photos which in turn contain a url to the gif
var url = Session.get('event').photos[Session.get("id")].url;
FB.ui({
method: 'share',
href: url
});
}
SERVER SIDE
JAVASCRIPT
if(Meteor.isClient) {
window.fbAsyncInit = function() {
FB.init({
appId : 'APP_ID',
status : true,
xfbml : true,
version : 'v2.5'
});
};
}
Edit: I found a manual solution using exec future and curl
so first I added a call to a meteor method on the share that updates the facebook crawler
JAVASCRIPT
Template.*templateName*.events({
'click .facebook': function(e){
e.preventDefault();
e.stopPropagation();
// this is in a modal so I store the data I need
// (events have photos which in turn contain a url to the gif
var url = Session.get('event').photos[Session.get("id")].url;
Meteor.call('updateCrawler', url, function(){
FB.ui({
method: 'share',
href: url
});
});
}
Then I defined my meteor method as such
JAVASCRIPT
Meteor.methods({
updateCrawler: function(url){
var future = new Future();
cmd = 'curl -X POST -F "id=' + url + '" -F "scrape=true" -F "access_token={my_access_token}" "https://graph.facebook.com"';
exec(cmd, function(error){
if (error){
console.log(error);
}
future.return();
});
future.wait();
}
});
it's ugly but since I'd have to wait for the crawler to update and it works I'll leave this here for future use for someone maybe
Edit2:
I did not use og tags at all since I was simply sharing a url to aws directly and not a url to my website
I worked around this problem by calling the Facebook API direct from the server to make it scrape the og data by requesting info on the page. First time round it doesn't have the image cached but second time it does so this workaround does the initial call before sharing.
Use an access token for your facebook app and call the below in an ajax call and await the response before opening share dialog. Replace Google address with your own uri encoded address https://graph.facebook.com/v2.5/?id=http%3A%2F%2Fwww.google.co.uk&access_token=xxxxx
EDIT:
As per comments, here is my server side method for calling this which I use when posts etc are inserted to make the initial call and prompt a scrape from fb:
var getTheOGInfo = function (link)
{
if (!link || link.slice(0, 4).toLowerCase() != "http"){
throw new Meteor.Error("og-info-bad-url", "Function requires an unencoded fully qualified url");
return false;
}
var url = "https://graph.facebook.com/v2.5/{{{{id}}}}?access_token={{{{token}}}}&fields=og_object{id,description,title,type,updated_time,url,image},id,share";
var token = Meteor.settings.private.fb.token;
if (!token){
throw new Meteor.Error("og-info-no-token", "Function requires a facebook token in Meteor.settings.private.fb.token");
return false;
}
var link_id = encodeURIComponent(link);
url = url.replace('{{{{token}}}}', token).replace('{{{{id}}}}', link_id);
var result = HTTP.get(url, {timeout:1000});
return result;
}
Or for your purposes you may not want anything that might be blocking so you could change the last two lines to be aynchronous:
var result = HTTP.get(url, {timeout:1000});
return result;
//Replace with non blocking
HTTP.get(url, {timeout:1000}, function(err, result){console.log('something asynchronous', err, result);});
return true;
How can I get jquery click/touch event such tap, click, touchstart etc too work on Facebooks in app browser? Seems impossible!
<script src="js/jquery.mobile.tap.min.js"></script>
$(function(){
var custom_event = $.support.touch ? "tap" : "click";
});
Also tried
var custom_event = "touchstart click";
And then like so:
$(document).on(custom_event,'#selector',function(){
// DO STUFF
});
Ha, Weird bug, it was the FB.login that didn't work. Changed to php redirect login for messenger with:
$isIosFBMessenger = (strpos($_SERVER['HTTP_USER_AGENT'], 'FBAN/MessengerForiOS') !== false) ? true : false;
The following code works in my desktop browser but not in Safari on my iPhone, it does display the alert() but doensn't play the audio. What am I doing wrong?
soundManager.url = '/assets/backend/swf/';
soundManager.preferFlash = false;
soundManager.useHTML5Audio = true;
soundManager.onready(function(){
function playSong(){
soundManager.createSound({
id: 'song_<?=$song->ID?>',
url: '/backend/song/play/<?=$song->ID?>',
type: 'audio/mp3'
}).play();
}
$('#play').click(function(){
alert('playSong()');
playSong();
});
});
From what I've seen, iPhone doesn't like "auto-played" sounds, and they require the sound to be played from user interaction.
In your case, the iOS browser must not like the fact that you're creating the sound AND THEN playing it back immediately.
Give this variant a try and see if it works for you:
soundManager.url = '/assets/backend/swf/';
soundManager.preferFlash = false;
soundManager.useHTML5Audio = true;
soundManager.onready(function(){
// Create the sounds here (don't call play)
soundManager.createSound({
id: 'song_<?=$song->ID?>', // It is not cool to put PHP here, read below!
url: '/backend/song/play/<?=$song->ID?>',
type: 'audio/mp3'
});
$('#play').click(function(){
var soundId = 'song_<?= $song->ID ?>';
soundManager.play(soundId);
});
});
As a side note: I'd advise you AGAINST mixing JS and PHP in that way. Check this SoundManager2 example on how to "augment" a regular MP3 link like this:
Click here to play blabla
into something that plays an MP3 onclick:
SoundManager2 documentation
Regards and good luck!
I'm trying to get the SoundCloud HTML5 player widget to automatically start and seek to a specific track and position but no matter what I try it doesn't work.
I'm using the API code below:
<iframe width="100%" height="450" scrolling="no" id="soundcloud-player" frameborder="no" src="https://w.soundcloud.com/player/?url=http%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F3058825&color=00be53&auto_play=false&show_artwork=true"></iframe>
<script type="text/javascript" src="http://w.soundcloud.com/player/api.js"></script>
<script type="text/javascript">
(function(){
var widgetIframe = document.getElementById('soundcloud-player'),
widget = SC.Widget(widgetIframe);
widget.bind(SC.Widget.Events.READY, function() {
widget.play();
widget.seekTo('5000');
});
widget.bind(SC.Widget.Events.PLAY, function() {
// get information about currently playing sound
widget.getCurrentSound(function(currentSound) {
console.log('sound ' + currentSound.title + 'began to play');
});
}); }());
What I'm basically trying to accomplish is have the player automatically seek to the same spot when the user switches between pages on the site. I plan on reading from a cookie, the position and track and then using the method above. Any help would be greatly appreciated!
The problem is most probably related to the sound not being fully loaded at the moment when you are trying to call seekTo. You can easily verify this by adding the following bit to your code:
// …
widget.bind(SC.Widget.Events.READY, function() {
widget.play();
// Note setTimeout here!
// This will now work since the needed part of the sound
// will have loaded after the timeout
setTimeout(function () {
widget.seekTo('5000');
}, 1000);
});
// …
But since you don't really want to have arbitrary timeout in your code, it's a good idea to attach event handler to progress event:
widget.bind(SC.Widget.Events.LOAD_PROGRESS, function onLoadProgress (e) {
if (e.loadedProgress && e.loadedProgress === 1) {
widget.seekTo(15000); // seek to previous location
widget.unbind(SC.Widget.Events.LOAD_PROGRESS);
}
});
Here's a working version of this code http://jsbin.com/ebeboj/2/edit
Also, in case you have very long tracks, you could also retrieve duration from the sound (via getCurrentSound), check at what point in range from 0 to 1 the track has stopped playing and only wait for that value (since loadedProgress === 1 might take a while), something like:
widget.getCurrentSound(function(currentSound) {
// currrentSound.duration is 269896 for the first track of your playlist
relativePreviousPlay = previousPlay / currentSound.duration; // ~0.204
});
widget.bind(SC.Widget.Events.LOAD_PROGRESS, function onLoadProgress (e) {
if (e.loadedProgress && e.loadedProgress > relativePreviousPlay) {
widget.seekTo(previousPlay); // seek to previous location
widget.unbind(SC.Widget.Events.LOAD_PROGRESS);
}
});
Check out working example for the last bit of code here http://jsbin.com/ebeboj/4/edit
Sidenote: I'd recommend using localStorage over cookies for storing previous position of playback, because cookies will travel back and forth from client to server slowing down your website, and you likely don't need the information on the sever side.