AngularJS Cordova Media Doesn't Always Play Next Song - javascript

I'm using the Ionic Framework with AngularJS and the ngCordova Media Plugin: http://ngcordova.com/docs/plugins/media/
I currently have two functions in my controller;
Play - this function plays the chosen media src
$scope.play = function(src) {
media = $cordovaMedia2.newMedia(src, null, null, mediaStatusCallback);
media.play();
$scope.isPlaying = true;
$ionicLoading.hide();
}
}
Next Song - this function gets the src of next song in the list by looking up the index of current song object and choosing the next object in the list.
$scope.nextSong = function (song_id) {
var index = $scope.allsongs.indexOf(song_id);
if(index >= 0 && index < $scope.allsongs.length - 1) {
$scope.nextsongid = $scope.allsongs[index + 1]
$scope.playMusic($scope.nextsongid); //the playMusic function just looks up the src of the song and parses to the play function
}
}
Here is the playMusic function which is called above:
$scope.playMusic = function(music_id)
{
myService.getSongInfo(music_id)
.then(function(data){
$scope.cursong = data.results;
$scope.play($scope.cursong.src);
});
};
And in my template, the next button just calls the nextSong function like so with the current song ID:
<div class="next" ng-click="nextSong('{{cursong.id}}')"></div>
The problem I'm facing is that when i press the next button first time, it plays the next song but when i press it again, it plays the same song again. It seems the 'currsong' value is not updated.
This is what the 'allsongs' array looks like:
["1631922", "1502059", "1365874", "1607940", "1251204", "1233296", "1151847", "1119737", "1086438", "1078140", "1068172", "1065312", "845339", "799161", "316293", "306073", "299817", "1603940"]
Update: I tried using an interval
I have an interval function which skips to next song when the time has reached the duration. I call the nextsong function there and it works perfectly.
$scope.Timer = $interval(function () {
$scope.cursongduration = $scope.cursong.duration;
$scope.cursongid = $scope.cursong.id;
if($scope.currduration >= $scope.cursongduration){
$scope.nextSong($scope.cursongid);
}, 1000);
};
So my question is why calling nextSong through the template button work as well? To me it seems like the cursong.id is not updated in the template after 1 time for some odd reason. I have also tried using cursongid variable which is set in the interval in the template button but no luck:
<div class="next" ng-click="nextSong('{{cursongid}}')"></div>

The problem isn`t in cordova or media plugin, when you play the song you have to update the $scope.cursong.id so the next click can know what is the current song.
$scope.play = function(src) {
$scope.cursong.id = src;
media = $cordovaMedia2.newMedia(src, null, null, mediaStatusCallback);
media.play();
$scope.isPlaying = true;
$ionicLoading.hide();
}
}
I`m assuming you have declared the $scope.cursong.id somewhere in your script.

I have resolved this issue by creating a new function 'PlayNextSong' which stores $scope.cursong.id into a new scope 'cursongid' and then calls the nextSong function. This works fine in my case.
The function:
$scope.playNextSong = function() {
$scope.cursongid = $scope.cursong.id;
$scope.nextSong($scope.cursongid);
}
The button:
<div class="next" ng-click="playNextSong()"></div>

Related

jQuery problem referencing id - Using Howler.js

I have a maze I'm struggling to sort:
I have this grid with 12 anchor tags
for (let i = 1; i <= 12; i++) {
$("#minuetti").append(
`<div class="col-6 col-md-3"> <a id="minuetto${i}" class="btn btn-primary btn-lg mb-1 minuetto">Minuetto ${i}</a></div>`
);
}
Each one of those will play a different mp3 file with the code below using Howler.js.
$(".minuetto").on("click", function() {
let id = this.id;
let minuettoPath = `assets/music/${id}.mp3`;
let cell = new Howl({
src: [minuettoPath],
onplay: function() {
$(`#${id}`).text("Stop");
$(".minuetto").addClass("disabled");
$(`#${id}`).removeClass("disabled");
$(`#${id}`).removeClass("minuetto");
$(`#${id}`).on("click", function() {
cell.stop();
});
},
onend: function() {
$(".minuetto").removeClass("disabled");
$(`#${id}`).text(`Minuetto ${id.slice(8)}`);
$(`#${id}`).addClass("minuetto");
this.unload();
}
});
cell.play();
});
My problem:
I'm trying to change the text of the button clicked and keep it enabled.
I'm disabling all remaining (11) buttons until the end of the mp3 file.
But when I click on the enabled button (the one playing) instead of stopping my mp3, it restarts.
What am I doing wrong with my jQuery here? I'm referencing to my id and calling the function to do cell.stop() but for some reason jQuery runs the playing again, even though I removed the class minuetto from the playing button.
I'm really puzzled. Please help?
Use just const sound = new Howl({src: mp3.path});
Create a element using jQuery's Object Element constuctor $({})
Assign the needed events like click(), play(), and stop() that you can trigger when needed and perform your desired actions
Use Howler's sound.playing() to determine which trigger callback is needed
// Use an array of objects instead, otherwise if one day
// you decide to remove a numeric song you'll have to replace it with another.
// This also allows you to change the order of songs:
const mp3list = [
{name:'Enter Sandman', path:'https://upload.wikimedia.org/wikipedia/en/3/39/Metallica_-_Enter_Sandman.ogg'},
{name:'Back in Black', path:'http://upload.wikimedia.org/wikipedia/en/4/45/ACDC_-_Back_In_Black-sample.ogg',},
{name:'U2 - One', path:'https://upload.wikimedia.org/wikipedia/en/5/5e/U2_One.ogg',},
];
let $allMP3 = [];
const $newMP3 = mp3 => {
// HOWL
const sound = new Howl({src: mp3.path});
// $ ELEMENT
return $('<div>', {
class: 'minuetto',
text: mp3.name,
on: {
click() {
$allMP3.forEach($el => $el.not(this).trigger('stop'));
$(this).trigger(sound.playing() ? 'stop' : 'play');
},
play() {
$(this).text("Stop");
sound.play();
},
stop() {
$(this).text(mp3.name);
sound.stop();
}
}
});
};
$allMP3 = mp3list.map($newMP3); // Populate array of $ elements
$("#minuetti").append($allMP3); // Append once!
<div id="minuetti"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.3/howler.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

trigger functions no longer working after implement ajax scroll pagination

I have developed an audio platform similar to Soundcloud and it all works(ed) perfectly! Until I decided to create an Ajax scroll pagination.
Both the pagination and Ajax work fine. However, I have noticed some JavaScript that used to work before implementing Ajax, which no longer does.
The script has a pretty simple duty; play a track when the user clicks the play button (or pause when the user clicks on it again). Then, once the track has finished, move on to the next track until it finally reaches the end.
What happens now is, when the page first loads up (along with the 10 tracks that load with the page), the script will work as it is supposed to. But, when the user scrolls down to get more results to load, if the user clicks on one of the newly loaded tracks play button, the track either won't play, or it will play over the other track which is supposed to pause (and then all the buttons just completely stop working).
Here is all of the feed.js (removed as much bloat code as possible, and placed comments):
$(document).ready(function(){ // on page load
var tp = 1; // set track page equal to one
loadTracks(tp); // then load all of the tracks
jQuery(function($) {
$('.f-outer-container').on('scroll', function() { // when the user scrolls to the bottom of the page load more tracks
if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
tp++;
loadTracks(tp);
}
});
});
function loadTracks(track_page){
$.post('/spectrum-rr/core/_func/functions/loadTrack.php', {'page': tp}, function(data){ // get send and get data from loadTrack.php
$("#f-ap__aj").append(data); // append those tracks in the feed
// player functions
$(".track-w__trigger").click(function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'), // get its track id
tiW = 'w' + tid + 'w',
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
});
}
});
// player functionality
function playPauseButton(button, wave, trackID){ // once the function has been called
pausePrevious(button); // pause the previous track (this doesn't work when more ajax results are loaded)
var button = $(button);
if(wave.isPlaying()){ // if the wave is playing; stop it
button.removeClass("playing");
wave.pause();
} else { // vice versa
button.addClass("playing");
wave.play();
}
var waveDuration = wave.getDuration();
var nextTrack = ++trackID;
var checkAudioFinished = setInterval(function(){ // check if the audio has finished playing every second
if(wave.getCurrentTime() >= waveDuration){ // if it has
button.removeClass("playing"); // remove it's buttons "playing" class
$('#w' + nextTrack + 'w-trigger').trigger('click'); // play the next song on the playlist by incrementing the id
clearInterval(checkAudioFinished);
}
}, 1000);
}
function pausePrevious(b){
$(".playing").not(b).each(function(){ // when this function is triggered
$(".playing").trigger('click'); // pause all of the other tracks (by simulating the click of their pause buttons
$(".playing").removeClass("playing"); // remove it's class too
});
}
I feel these problems are occurring due to the use of $(document).ready();. Forcing these functions to only be available to those tracks that were already loaded. However, I am not sure.
Here is the HTML that gets sent back from each request (in 10s):
<div class="f-wave-send f-waveform-container">
<div aria-trackid="1" class="track-w__trigger" id="w1w-trigger"></div> <!-- the "1" is generated by PHP. it is incremented for every div !-->
<div class="f-waveform-outer-container">
<div aria-trackid="1" class="track-w__waveform" id="w1-w"></div>
<script>
var w1w = WaveSurfer.create({ // wavesurfer script (this is the "wave" object that is being triggered inside the playPauseButton() function !-->
container: '#w1-w',
barWidth: 2,
});
w1w.load('./player/audio/sampleaudio.mp3');
</script>
</div>
</div>
If anyone could give me insight as to what might be going on (or any tips in improving my code for that matter), it would be greatly appreciated!
Try this,
You should not bind an click event each time load happens and it should be moved out of your loadTracks, instead you shuold apply event delegation.
// player functions
$(document).on('click', '.track-w__trigger', function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'), // get its track id
tiW = 'w' + tid + 'w',
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
Change your code to this
$(document).ready(function(){ // on page load
var tp = 1; // set track page equal to one
loadTracks(tp); // then load all of the tracks
jQuery(function($) {
$('.f-outer-container').on('scroll', function() { // when the user scrolls to the bottom of the page load more tracks
if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
tp++;
loadTracks(tp);
}
});
});
function loadTracks(track_page){
$.post('/spectrum-rr/core/_func/functions/loadTrack.php', {'page': tp}, function(data){ // get send and get data from loadTrack.php
$("#f-ap__aj").append(data); // append those tracks in the feed
});
}
// player functions
$(".track-w__trigger").on("click",function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'); // get its track id
tiW = 'w' + tid + 'w';
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
});
Just move out $(".track-w__trigger").click outside $.post and change it to $(".track-w__trigger").on("click",function()

How can I play a sound on all devices when an element is added to a scope in AngularJS

I want to notify all users of a web app that a new element has been added to a $scope by playing a beep. So when a new element turns up in an ng-repeat the app goes beep.
I can do that easily enough on the screen where the element is being added (as shown below) but can't find out how to play a beep for everyone.
$scope.beep = function(){
beep.play();
};
$scope.addMeal = function(meal) {
if(meal.food != null && meal.calories != null){
$scope.todaymeals.$add(meal);
if(angular.isDefined($scope.meal)){
delete $scope.meal;
beep.play();
}
}
}
You can just easily call the onchildadded method:
ref.on('child_added', function(childSnapshot, prevChildKey) {
beep.play(); //In Your case
});

JS/JQuery text to speech: How to chain audios?

Given n different words:
var text = [ "bèijing Beijing Shanghai"]
var words= [ "bèijing", "Beijing", "Shanghai" ];
Given n different .ogg audio files with a known locations :
<!-- AUDIOS FILES -->
<audio id="id1" src="/audio/Zh-bèijing.ogg"></audio>
<audio id="id2" src="/audio/Zh-Beijing.ogg"></audio>
<audio id="id3" src="/audio/Zh-Shanghai.ogg"></audio>
I use JS getElementById('...').play(), which I run using an onclick event over an HTML element:
<!-- HTML -->
<div id="play" onClick="document.getElementById('id1').play();">
<button>Play</button>
</div>
This allow me to play one audiofile.
Using one single onlick event, how play the first audio, then when done chain to it the play of a second audio ?
Starting fiddles with assets there : http://jsfiddle.net/J9wAB/
Note: I tried document.getElementById('id1').play().done(document.getElementById('id2').play()), but it plays both files simultaneously.
EDIT: I accepted one answer working on Firefox. Yet, this addEventListener based answer fails on on some browsers. An alternative promise base answer may be more successful.
Something like this :
$('#play').data('audio', $('audio:first')).on('click', function() {
var self = this;
$(this).data('audio').on('ended', function() {
var n = $('audio').eq( $(self).data('audio').index('audio') + 1 );
$(self).data('audio', n.length ? n : $('audio:first'));
}).get(0).play();
});
FIDDLE
It gets the first audio element in the document, and stores it in jQuery's data, and once it's played (the onended event) it get's the next audio element in the DOM and the next time the button is clicked, it playes that, and when there are no more audio elements, it starts from the beginning again.
If you really wan't to use the ID instead, it would be something like :
var audio = 1
$('#play').on('click', function() {
$('#id' + audio).get(0).play();
audio++;
});
FIDDLE
To play all three on a single click, one after the other, you'd do
$('#play').on('click', function() {
$('audio').on('ended', function() {
$('audio').eq($(this).index('audio')+1).get(0).play();
}).get(0).play();
});
FIDDLE
No jQuery needed, just listen for the ended event
The simple case is the following:
document.getElementById('id1').addEventListener('ended', function(){
document.getElementById('id2').play();
});
Abstracted, it would look like http://jsfiddle.net/J9wAB/13/
function chain(ids/* id, id, ... */) {
function setHandler(first, next) {
document.getElementById(first).addEventListener('ended', function(){
document.getElementById(next).play();
});
}
for (var i = 0; i < arguments.length - 1; i++) {
setHandler(arguments[i], arguments[i+1]);
}
}
chain('id1', 'id2', 'id3');
You can use the onended="yourcallback()" to start the next audio.
Look at this answer please: https://stackoverflow.com/a/7652194/2707424

How Can I Insert My Javascript Into My Drupal Site/Node

I'm trying to insert a cookie that is provided by a video host that will resume a video where the user left off. They have an example that obviously works. When trying to insert this into my Drupal site, the cookie won't work. The video just starts back at the beginning.
I have enabled "PHP input filter", as I read that I needed to do that for drupal to insert the script. Please see the code that is in my node below.
Can anyone help me figure out why this isn't working, how to get it to work, or a better way of doing this with Drupal?
Thank you,
<script type="text/javascript">
wistiaEmbed.ready( function() {
var all_cookies = document.cookie.split(';'), // gets the value of the cookies on the page
cookie_str = "resume_video=",
resume_cookie = does_resume_cookie_exist(all_cookies);
function does_resume_cookie_exist(cookie_arr) {
var i, curr_string, found;
for (i = 0; i < cookie_arr.length; i++) {
curr_string = cookie_arr[i];
if (curr_string.substring(0,5) === cookie_str.substring(0,5)) {
// we've found it!
found = curr_string;
break;
}
}
return found;
}
function set_cookie_time(t) {
document.cookie = cookie_str + t.toString(); // this takes the time (t) and sets the cookie with that time
}
if (resume_cookie) {
num = resume_cookie.split('=')[1];
start_time = parseInt(num);
wistiaEmbed.time(start_time).play(); // plays the video at the specific time defined in the cookie upon return to the page
} else {
set_cookie_time(0); // places a cookie on the visitor
wistiaEmbed.play(); // starts the video from the beginning
}
wistiaEmbed.bind("timechange", function(t) { // on timechange, reset cookie
set_cookie_time(t);
});
wistiaEmbed.bind("end", function() { // if person has watched the entire video, sets the video to beginning upon retun
set_cookie_time(0);
});
});
</script>
<div id="wistia_npcc5k96s9" class="wistia_embed" style="width:640px;height:508px;"> </div>
<script charset="ISO-8859-1" src="http://fast.wistia.com/assets/external/E-v1.js"> </script>
<script>
wistiaEmbed = Wistia.embed("npcc5k96s9");
</script>**strong text**
What version of drupal are you using? Does the code that you gave actually output in your request response?
There are several solutions to this (IMO).
If the code is showing up in the response, it could be some other javascript error that preventing your code from executing.
If that snippet of code is applicable to all nodes of that type you can use the node_view hook in order to inject your code on that node type, for example (I am assuming D7):
function mymodule_node_view($node, $view_mode)
{
if($node->type =='video_page')
{
drupal_add_js('resume_video.js'); // this js holds your code snippet
}
}
Here's a reference that could help you out
https://api.drupal.org/api/drupal/modules%21node%21node.api.php/function/hook_node_view/7
You can similarly inject that snippet of code at the theme layer using a theme hook, probably hook_preprocess https://api.drupal.org/api/drupal/modules!system!theme.api.php/function/hook_preprocess/7
Hope that helps.

Categories

Resources