Page re-renders on collection update - javascript

I am making a simple multiple user playlist application. I have one client watching the stream of videos, and in another I am trying to add a video to that playlist. The video adds to the playlist correctly, but when the update happens the page re renders and the video restarts on the client that is just watching. I am trying to get it so that when other users add videos to the playlist, it continues to play for all who are watching it.
On the server I have:
Meteor.publish('videosQue', function (room_id) {
return Videos.find({room_id: room_id});
});
And on the client I set it up like the todo example:
var videosHandle = null;
Deps.autorun(function(){
var room_id = Session.get('room_id');
if (room_id)
videosHandle = Meteor.subscribe('videosQue', room_id);
else
videosHandle = null;
});
And then here is some code that applies to when a person is in a room:
Template.EnteredRoom.loading = function () {
return videosHandle && !videosHandle.ready();
};
Template.EnteredRoom.room = function () {
return Rooms.findOne(Session.get('room_id'));
};
Template.EnteredRoom.video_objs = function () {
Session.set('videos', Videos.findOne({room_id: Session.get('room_id')}))
return Session.get('videos');
};
Template.EnteredRoom.rendered = function () {
if (Session.get('videos') && Session.get('videos').videoIds.length) {
var currVid = 0, playlist = Session.get('videos').videoIds;
renderVid(playlist, currVid);
};
};
var renderVid = function(playlist, currVid) {
var video = Popcorn.youtube('#youtube-video', 'http://www.youtube.com/embed/' + playlist[currVid] + '&autoplay=1');
video.on("ended", function() {
if (++currVid < playlist.length)
renderVid(playlist, currVid);
else {
$('#youtube-video').hide();
$('#video-container').append('<div style="font-size:30px;color:white;">No Videos :( Try adding one!</div>');
}
});
}
Is there anyway to get this done?
Thanks for the Help!
Andrew
EDIT:
After doing some reading I think that I should somehow use isolate to achieve this, any advice?

I think you can try putting the part that you don't need reactive in the {{#constant}} block.
{{#constant}}
{{#each ...}}
....
{{/each}}
{{/constant}}
I prefer to do it disable the reactive from collection with { reactive: false } option, in your model function:
return collection.find({}, {reactive:false}) }

Related

How to run the animation only once per page?

How can I change the following code to run only once per session, and once per page. The div#edgtf-manon-loading-title appears on every page. So, on the second time the same user/session goes to the same page they went before, the animation would NOT load anymore. Thanks!
function edgtfLoadingTitle() {
var loadingTitle = $('#edgtf-manon-loading-title');
if (loadingTitle.length) {
var fallback = edgtf.body.hasClass('edgtf-ms-explorer') ? true : false;
var done = function() {
edgtfEnableScroll();
edgtf.body.addClass('edgtf-loading-title-done');
$(document).trigger('edgtfTitleDone');
};
var toTop = function() {
loadingTitle
.addClass('edgtf-to-top')
.one(edgtf.transitionEnd, done);
};
edgtfDisableScroll();
loadingTitle.addClass('edgtf-load');
if(fallback) {
toTop();
}
loadingTitle.one(edgtf.animationEnd, toTop);
}
}
Is there a way I can add something like this code below to the original? And where exactly?
var yetVisited = localStorage['visited'];
if (!yetVisited) {
//something here
};

How to have multiple instances of a jQuery plugin

I am trying to create a SoundCloud music player. It can play any track from SoundCloud, but this plugin is only working if there is only one instance of it in the page. So it wont work if two of the same plugin are in the page.
Here is an example of having two players in the page: JSFiddle
var trackURL = $(".player").text();
$(".player").empty();
$(".player").append("<div class='playBTN'>Play</div>");
$(".player").append("<div class='title'></div>");
var trackId;
SC.get('/resolve', { url: trackURL }, function (track) {
var trackId = track.id;
//var trackTitle = track.title;
$(".title").text(track.title);
$(".playBTN").on('click tap', function () {
//trackId = $(".DSPlayer").attr('id');
stream(trackId);
});
// first do async action
SC.stream("/tracks/" + trackId, {
useHTML5Audio: true,
preferFlash: false
}, function (goz) {
soundToPlay = goz;
sound = soundToPlay;
scTrack = sound;
//updater = setInterval( updatePosition, 100);
});
});
var is_playing = false,
sound;
function stream(trackId) {
scTrack = sound;
if (sound) {
if (is_playing) {
sound.pause();
is_playing = false;
$(".playBTN").text("Play");
} else {
sound.play();
is_playing = true;
$(".playBTN").text("Pause");
}
} else {
is_playing = true;
}
}
If you remove any of these div elements that hold the .player class, the other element will work. So it only doesn't work because there are two instances of the same plugin.
How can I fix it? to have multiple instances of the player in one page?
I have identified the problem. It has to do with the fact that you are trying to load multiple tracks at the same time, but have not separated the code to do so.
As #Greener mentioned you need to iterate over the .player instances separately and execute a SC.get() for each one of them.
Here is what I see happening that is causing the problem:
var trackURL = $(".player").text();
^The code above returns a string that contains both of the URLs you want to use back-to-back without spaces. This creates a problem down the road because of this code:
SC.get('/resolve', { url: trackURL }, function (track) {...
That is a function that is trying to load the relevant song from SoundCloud. You are passing it a variable "trackURL" for it to try and load a specific URL. The function gets a string that looks like "URLURL" what it needs is just "URL".
What you can do is iterate over all the different ".player" elements that exist and then call the sounds that way. I modified your script a little to make it work using a for loop. I had to move the "empty()" functions into the for loop to make it work correctly. You have to use .eq(index) when referring to JQuery array of elements.
Like this:
var trackURL
var trackId;
for(index = 0; index < $(".player").length; index++){
trackURL = $(".player").eq(index).text();
//alert(trackURL);
$(".player").eq(index).empty();
$(".player").eq(index).append("<div class='playBTN'>Play</div>");
$(".player").eq(index).append("<div class='title'></div>");
SC.get('/resolve', { url: trackURL }, function (track) {
var trackId = track.id;
alert(track.id);
//var trackTitle = track.title;
$(".title").eq(index).text(track.title);
$(".playBTN").eq(index).on('click tap', function () {
//trackId = $(".DSPlayer").attr('id');
stream(trackId);
});
// first do async action
SC.stream("/tracks/" + trackId, {
useHTML5Audio: true,
preferFlash: false
}, function (goz) {
soundToPlay = goz;
sound = soundToPlay;
scTrack = sound;
//updater = setInterval( updatePosition, 100);
});
});
}
This is not a completely finished code here, but it will initiate two separate songs "ready" for streaming. I checked using the commented out alert what IDs SoundCloud was giving us (which shows that its loaded now). You are doing some interesting stuff with your streaming function and with the play and pause. This should give you a good idea on what was happening and you can implement your custom code that way.

load multiple audio files to be played simultaneously even with poor internet connection [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have developed a functional multi-track audio player, and if i am connected to wifi, this player works as intended. I need the files to play simultaneously. But, if I am on my phone, it plays the first file, then the second, then third and so on. Is there a way i can send all selected files to the server first, then send it back to the user to be listened to all together? Please help. Need to get this site launched as soon as possible.
makeSong = function(f) {
var add, button, path, play;
path = curpath.concat(["" + f + ".mp3"]).join('/');
button = function(cls, onclick) {
return $('<i/>').addClass(cls).addClass('link').addClass('fa').addClass("fa-" + cls).click(onclick);
};
play = button('play', function() {
play = $(this).hasClass('fa-play');
if (play) {
multiPlayer.stop();
singlePlayer.play1(path);
} else {
singlePlayer.stop();
}
$(this).toggleClass('fa-play', !play);
return $(this).toggleClass('fa-pause', play);
});
add = button('plus', function() {
return addItem(f, path);
});
return $('<div/>').addClass('sound cf').text(f).append($('<div/>').addClass('buttons left').append(play).append(add));
};
addSongs = function(fnames) {
var $songs, f, _i, _len, _results;
$songs = $('.sounds').empty();
_results = [];
for (_i = 0, _len = fnames.length; _i < _len; _i++) {
f = fnames[_i];
_results.push(makeSong(f).appendTo($songs));
}
return _results;
};
currentPath = [];
loadSongs = function(path) {
return $.getJSON('/api/dir', {
parts: JSON.stringify(path)
}, function(d) {
curpath = path;
return addSongs(d.fnames);
});
};
parentFolder = function(x) {
return x.parents('.folder').first();
};
getPath = function(elem) {
var parent;
parent = parentFolder(elem);
if (parent.length === 0) {
return [];
}
return getPath(parent).concat([parent.attr('data-folder')]);
};
$('.categories .folder span.link').click(function() {
loadSongs(getPath($(this)));
$('.categories .folder').removeClass('active');
return parentFolder($(this)).addClass('active');
});
$('.player .play').click(function() {
var $i, play;
$i = $(this).find('i');
play = $i.hasClass('fa-play');
if (play) {
multiPlayer.playAll();
} else {
multiPlayer.stop();
}
$i.toggleClass('fa-play', !play);
return $i.toggleClass('fa-pause', play);
});
$('.player .sound').click(function() {
var $i, mute;
$i = $(this).find('i');
mute = $i.hasClass('fa-volume-up');
if (mute) {
multiPlayer.mute();
} else {
multiPlayer.unmute();
}
$i.toggleClass('fa-volume-up', !mute);
return $i.toggleClass('fa-volume-off', mute);
});
$('.button.buy').click(function() {
var x;
x = [];
$('.things .thing').each(function() {
return x.push($(this).attr('data-path'));
});
if (x.length > 0) {
return window.location.href = "/buy?" + ($.param({
songs: JSON.stringify(x)
}));
} else {
return alert('Please add some songs below first.');
}
});
$('.folder .link').click(function() {
return $(this).closest('.folder').children('.subfolders').toggle();
});
}).call(this);
here is a simple example of collecting and syncing four tracks. for a full app, you'll probably want to enable a button or something in allLoaded(), instead of just play()'n them all.
var tracks=[ // array of urls of mp3s
"https://archive.org/download/tsp1997-01-24.AT.TC-D5M.flac16/tsp1997-01-24d1t05.mp3",
"https://archive.org/download/tsp1997-01-24.AT.TC-D5M.flac16/tsp1997-01-24d2t02.mp3",
"https://archive.org/download/tsp1997-01-24.AT.TC-D5M.flac16/tsp1997-01-24d2t03.mp3",
"https://archive.org/download/tsp1997-01-24.AT.TC-D5M.flac16/tsp1997-01-24d1t10.mp3"
],
loadedCount=0, //how many tags are ready to play through?
tags=[]; // collection of <audio> tags
function allLoaded(){ //all tags can play, do something about it:
tags.forEach(Function.call.bind(tags[0].play));
}
tracks.forEach(function(a){
var aud=new Audio();
tags.push(aud); // add this track to collection
aud.preload=true; // important
aud.controls=true; // only so we can see the player's time display to verify it works
aud.oncanplaythrough=function(e){
loadedCount++; //one more tags came in
if(loadedCount===tracks.length) allLoaded(); //done loading
};
document.body.appendChild(aud); // needed to see time
aud.src=a; // load the mp3 url
console.log("loading audio tag: ", aud);
});
obligatory fiddle demo: https://jsfiddle.net/138vd2oc/
in term of paying, i don't care about money so please donate any extra money you have to a worthy cause, or at least give a random beggar $20 for me.

knockout dirty flag code not working

Just started with knockout and need to implement page change warning. Following is the code snippet. I just need an alert pop up as warning if any change is made on the page.
function parseViewModel() {
var viewModel = JSON.parse(getState());
viewModel.checking = ko.observable(false);
viewModel.Slider = new ko.observable(100 - viewModel.Slider);
viewModel.CausalsList = buildHierarchy(viewModel.Causals);
viewModel.Causals["-1"] = "Total Marketing Budget";
viewModel.GeographiesList = ko.observableArray(gl);
viewModel.Geographies["0"] = "All Geographies";
viewModel.ProductsList = ko.observableArray(pl);
viewModel.Products["0"] = "All Products";
.
.
.
return viewModel;
}
function bindModel() {
model = parseViewModel();
ko.dirtyFlag = function (root, isInitiallyDirty) {
var result = function () { },
_initialState = ko.observable(ko.toJSON(root)),
_isInitiallyDirty = ko.observable(isInitiallyDirty);
result.isDirty = ko.computed(function () {
return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
});
result.reset = function () {
_initialState(ko.toJSON(root));
_isInitiallyDirty(false);
};
return result;
};
model.dirtyFlag = new ko.dirtyFlag(model);
model.isDirty.subscribe(function () {
alert("Page change warning!");
});
ko.applyBindings(model, $('#const').get(0));
ko.applyBindings(model, $('#buttonDiv').get(0));
}
Referred Ryan Niemeyer's blog. Unfortunately, it's not working anymore. Any insights please?
You would want to subscribe to model.dirtyFlag.isDirty in your case rather than model.isDirty.
One way to do is by using customBinding. I'm not that familiar with KO either but this might be something you're interested on.
Basically you would do is :-
ko.bindingHandlers.myFunction = {
update : function(){
//do something
}
}
http://knockoutjs.com/documentation/custom-bindings.html
And call it on your element using :-
<h1 data-bind="myFunction:{}"></h1>
Also, a jsfiddle to show how it works. (If you change the value of the First Name and focus out of it then the customBinding gets triggered. )
http://jsfiddle.net/3vuTk
Not sure if it's the best practice though.

how to play a sound file after another sound in javascript

How do I play a sound file after another sound ends? If I do it like
first.play();
second.play();
Both the sounds play at the same time. I want to play first.play() and when it ends, starts second.play().
first.addEventListener('ended', function(){
second.play();
});
first.play();
I would recommend kind of something:
// if using jQuery:
var audios = $('audio');
audios.each(function(idx){
$(this).bind('ended', function(){
var next = audios.get(idx+1);
if(next){
next.get(0).play();
}
});
});
You can also use a promise with a timeout:
const playPromise = first.play()
playPromise.then(() => {
setTimeout(() => {second.play()}, first.duration * 1000)
})
I used the AudioScheduledSourceNode.onended.
I used four sounds, and put this code in a function:
sound1.play();
sound1.onended = function () {
sound2.play();
};
sound2.onended = function () {
sound3.play();
};
sound3.onended = function () {
sound4.play();
}
Here is the app live: insultinator-toy.netlify.app
Click the question mark button.
And the link to the ugly source code. jsahlsa

Categories