I have a Google Action that plays some pre-recorder stories. I use mediaObject to play them. Something like:
conv.ask(new MediaObject({
name: title,
url: storia,
description: autore,
image: new Image({
url: largeImageUrl,
alt: title,
}),
}));
I since some stories are longer than 4 minutes, I've split them in two, and use a media status control:
app.intent('Media Status', (conv) => {
const mediaStatus = conv.arguments.get('MEDIA_STATUS');
if (mediaStatus && mediaStatus.status === 'FINISHED') {
console.log(`track finished: ${conv.user.storage.track}`);
console.log(`storia2: ${conv.data.storia2}`);
if (conv.data.storia2 !== undefined) {
secondaParte = `<audio src="` + randomPhrase(continuaFavola) + `">
<desc>Vediamo come va a finire...</desc>
</audio>`;
storia = conv.data.storia2;
conv.data.storia2 = undefined;
conv.ask(new SimpleResponse('<speak>' + secondaParte +'</speak>'));
} else {
conv.close(new SimpleResponse('<speak>' + goodnight +'</speak>'));
}
} else {
conv.ask('Media Status ignoto');
}
});
It works.
The problem is that sometimes, while the media object is playing, if I say the "stop" command, the media object stops, but the stop intent is not triggered (it should say "Ok, goodbye" and close the action), so the action remains waiting for a prompt. A second stop actually execute the stop intent. So basically I need to stop it twice.
In the log I can't see any trace of the first stop command (that has been clearly received since the media object has stopped), only the second one.
Even worse, when I play the second half of the story, this time the command is supposed to close:
conv.close(new MediaObject({ ... );
If I say "OK Google, stop", instead of stopping the playback, the action ends but the media object keeps playing (I know the action is quit because I can give standard commands, like "what's the time" or "set an alarm".
What a mess.
So is there a way to close the action, and force stop of any currently playing media object?
Related
I am making a battle bot using discordjs and I'm having some difficulties in updating the message according to the logs that my bot created for the battle. Here's the current code I'm using.
let interval = setInterval(async () => {
if(turnCounter <= battle.logs.length){
let currPlayer = turnCounter % 2 === 0 ? p1 : p2;
embed
.setTitle(("Round ", turnCounter))
.setDescription(`
**Round Summary** \n
${currPlayer.toString()} used **${battle.textlogs[turnCounter][turnCounter % 2][0].name}** and \ndealt **${battle.textlogs[turnCounter][turnCounter % 2][0].damage}** \nand \nhealed **${battle.textlogs[turnCounter][turnCounter % 2][0].heal}**!`);
embed.fields[0] = {name: "Player 1", value: `${message.author.username} \n Class: ${battle.player1.class.name} \n HP: **${battle.health_log[turnCounter][0]}** (${battle.health_log[turnCounter][0] - battle.health_log[turnCounter-1 >= 0 ? turnCounter-1 : 0][0]})`, inline: true};
embed.fields[1] = {name: "Player 2", value: `${user.username} \n Class: ${battle.player2.class.name} \n HP: **${battle.health_log[turnCounter][1]}** (${battle.health_log[turnCounter][1] - battle.health_log[turnCounter-1 >= 0 ? turnCounter-1 : 0][1]})`, inline: true};
let t = Date.now();
m = await m.edit(embed);
console.log(`It took ${Date.now()-t}ms for request to travel!`);
turnCounter++;
}
else clearInterval(interval);
}, 1000);
This approach resulted in the following log:
It took 179ms for request to travel!
It took 1879ms for request to travel!
It took 1045ms for request to travel!
It took 197ms for request to travel!
There are two problems that I'm facing here.
1 ) I'm waiting 1000ms to perform the code and awaiting message update takes 200-2000ms. It's too much. I want it to be around 200ms all time. Total of 1000+200ms is pretty okay. How should I deal with this?
2 ) When there are many concurrent battles happening, they all wait each other's await to continue execution. If I'm supposed to await these messages, I need to use clusters to fork the process but when I fork, multiple discordjs instances open up and bot connects to discord for several times and responds to commands several times too. How am I supposed to handle this problem?
My attempts:
Current one
While loop
I tried to make in a while loop instead of setInterval however it behaved the same.
Not awaiting
I tried m = m.edit(embed) instead of m = await m.edit(embed), this time it suddenly ended the battle and sent the last battle log to the discord.
I'm interested in getting a screenshot from the user and I'm using the getDisplayMedia API to capture the user's screen:
const constraints = { video: true, audio: false };
if (navigator.mediaDevices["getDisplayMedia"]) {
navigator.mediaDevices["getDisplayMedia"](constraints).then(startStream).catch(error);
} else {
navigator.getDisplayMedia(constraints).then(startStream).catch(error);
}
When executed, the browser prompts the user if they want to share their display. After the user accepts the prompt, the provided callback receives a MediaStream. For visualization, I'm binding it directly to a element:
const startStream = (stream: MediaStream) => {
this.video.nativeElement.srcObject = stream;
};
This is simple and very effective so far. Nevertheless, I'm only interested in a single frame and I'd therefore like to manually stop the stream as soon as I've processed it.
What I tried is to remove the video element from the DOM, but Chrome keeps displaying a message that the screen is currently captured. So this only affected the video element but not the stream itself:
I've looked at the Screen Capture API article on MDN but couldn't find any hints on how to stop the stream.
How do I end the stream properly so that the prompt stops as well?
Rather than stopping the stream itself, you can stop its tracks.
Iterate over the tracks using the getTracks method and call stop() on each of them:
stream.getTracks()
.forEach(track => track.stop())
As soon as all tracks are stopped, Chrome's capturing prompt disappears as well.
start screen capture sample code:
async function startCapture() {
logElem.innerHTML = "";
try {
videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
dumpOptionsInfo();
} catch(err) {
console.error("Error: " + err);
}
}
stop screen capture sample code:
function stopCapture(evt) {
let tracks = videoElem.srcObject.getTracks();
tracks.forEach(track => track.stop());
videoElem.srcObject = null;
}
more info: MDN - Stopping display capture
whenever default fallback intent is triggered 3 times in a row conversation is getting closed with the response, my test app left the conversation. How can i prevent this from happenning and also only exit from the action only when user wants to exit from the action.
The Actions on Google docs mention a conv.data.fallbackCount object, which you can read about in the No-match reprompting subsection:
app.intent('Default Fallback Intent', (conv) => {
conv.data.fallbackCount++;
// Provide two prompts before ending game
if (conv.data.fallbackCount === 1) {
conv.contexts.set(DONE_YES_NO_CONTEXT, 5);
conv.ask('Are you done playing Number Genie?');
} else {
conv.close(`Since I'm still having trouble, so I'll stop here. ` +
`Let’s play again soon.`);
}
});
Although it's arguably better conversation design to leave this variable alone -- thereby letting the conversation close when the counter increments to three -- you could try resetting conv.data.fallbackCount manually, like:
conv.data.fallbackCount = 0;
At least right now with the new Google Action you can just redirect the user to the current scene again on a NO_MATCH intent.
Basically in your webhook just set:
webhookResponseJson.scene.next.name = webhookRequestJson.scene.name
How is it possible to carry out any background tasks while Alexa is playing something with audioPlayer.Play?
The below plays an audio stream, but I need to perform other tasks in the background without intervention while the stream is playing. I know it is possible because other Skills can do it.
var handlers = {
'LaunchRequest': function() {
this.emit('Play');
},
'Play': function() {
this.response.speak('Sure.').
audioPlayerPlay(
'REPLACE_ALL',
stream.url,
stream.url,
null,
0);
this.emit(':responseReady');
}
}
Does anyone know or have any suggestions? From what I can see, once it starts playing the stream, I cannot get it to do anything unless I interrupt the stream to command another intent?
Alexa has a few built in requests that she sends to your skill throughout the lifecycle of an Audio stream for just this purpose! They are as follows:
AudioPlayer.PlaybackStarted - Sent when a new audio item begins playing.
AudioPlayer.PlaybackNearlyFinished - Sent when an audio item is almost over (most commonly used by a skill service to handle queuing the next item.)
AudioPlayer.PlaybackFinished - Sent when an audio item ends.
There are a couple of other ones too that you can read about here, but my guess is these will do just fine for what you need.
To use them, just set up handlers in your code for any of these requests and perform any tasks you need to there!
I don't know node.js at all, but my guess is the end result will look relatively close to this:
var handlers = {
'LaunchRequest': function() { /* your launch request code from above */ }
'Play': function() { /* your play code from above */ }
'AudioPlayer.PlaybackNearlyFinished': function() {
// Perform any background tasks here
}
}
Working with Spotify Web API and Web Playback SDK, does anybody know a good way to detect when a track's playback has finished, i.e. the song is over?
I know about some event named player_state_changed in Web Playback SDK, but the data provided in the event doesn't seem to be very helpful to me.
In my web app, the next song to be played is determined pretty spontaneously. It would be nice, if I could determine it only, when the current track has reached its end. Hence, there is no playlist. Instead, the app plays single tracks in series and should determine the next song, when the current one has ended.
Meanwhile I try it with this code here:
var bCallingNextSong = false;
player.addListener('player_state_changed', state => {
if (state != null &&
state.position == 0 &&
state.duration == 0 &&
state.paused == true) {
if (!bCallingNextSong) {
playNextSong();
}
}
});
function playNextSong() {
$.ajax({
url:"playback.php", //the page containing php script
type: "post", //request type,
dataType: 'json',
data: {action: "next", device: deviceId},
success: function(result) {
bCallingNextSong = false;
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
bCallingNextSong = false;
}
});
}
and in playback.php:
switch ($action) {
case "next":
//some blackbox magic here to determine next song, do logging etc.
//but for now just play the same song over and over
$api->play($deviceId, ['uris' => "spotify:track:0eGsygTp906u18L0Oimnem"],]);
echo json_encode(array("answer"=>"played next"));
break;
// more code
}
However, this will often (but not always) throw an InvalidStateError (I haven't found out where exactly yet nor why). Stack says dequeueUpdates / tryUpdate / _processUpdate / _appendUpdate. Yet, I'm a beginner and I've gotta learn how to cope with Firefox Debugger first.. :)
My last answer got broken so I will now add new solution which work for now.
this.player.addListener('player_state_changed', (state) => {
console.log(state);
if (
this.state
&& state.track_window.previous_tracks.find(x => x.id === state.track_window.current_track.id)
&& !this.state.paused
&& state.paused
) {
console.log('Track ended');
this.setTrackEnd(true);
}
this.state = state;
});
So what is different from last solution? I check that current_track is in previous track list. But there was problem now with position check because it seems that current track appears to previous track list before track is completely end. But that wasn't problem in my app because there is always small delays with commands.
EDIT: This is not working anymore for some reason. Check my other answer which will work now. https://stackoverflow.com/a/51114848/8081009
Here is my solution. It works most of the time. Actually I didn't get it broken in any scenarios I can imagine. So hold last state and check that is it gone to paused state. Hopefully Spotify will ease our work with track ended event. It would be great if they does. This is still better than polling their /me/player/currently-playing endpoint for every 1 second or so.
this.player.addListener(
'player_state_changed',
state =>
{
console.log(state);
if(this.state && !this.state.paused && state.paused && state.position === 0) {
console.log('Track ended');
this.setTrackEnd(true);
}
this.state = state;
}
);
I had this same problem, this is how I solved it.
When a song finish playing it gets added to the state.track_window.previous_tracks list
state.track_window.previous_tracks gets populated only if there is a next track and it starts playing
But every time you call the api to play a new track, it clears the state.track_window.previous_tracks & state.track_window.next_tracks list.
If there is a next track, I want to stop and choose my own next track and then call the api
var alreadyPlayed = state.track_window.previous_tracks
if(alreadyPlayed.length > 0) {
player.pause()
}
if there is no next track the player will be paused when the song finish
if(state.paused && state.position === 0) {
//song ended
ChooseNextSong()
}
the state callback gets fired many times but I want to call ChooseNextSong()
only once, so I increment startCheck every time it fires
if(state.paused && state.position === 0) {
//song ended
startCheck++
if(startCheck === 1) {
ChooseNextSong()
}
}
check if song started
if(state.position === 0 && alreadyPlayed.length === 0 && !state.paused)
{
//song started
startCheck = 0
}
You should use the player_state_changed event and look at which track is playing to see if the track has changed. You can find the current track in playerStateObject.track_window.current_track.
A possible implementation could look like this:
let currentTrack = '';
player.on('player_state_changed', state => {
if( state.track_window.current_track.id != currentTrack ) {
// The track changed!
currentTrack = state.track_window.current_track.id;
}
});