diff --git a/channel.js b/channel.js index 9e2abf55..e49b030c 100644 --- a/channel.js +++ b/channel.js @@ -276,8 +276,7 @@ Channel.prototype.userJoin = function(user) { // Set the new guy up this.sendPlaylist(user); - if(user.playerReady) - this.sendMediaUpdate(user); + this.sendMediaUpdate(user); user.socket.emit("queueLock", {locked: this.qlocked}); this.sendUserlist(user); this.sendRecentChat(user); @@ -473,26 +472,26 @@ Channel.prototype.broadcastMotd = function() { // The server autolead function function mediaUpdate(chan, id) { // Bail cases - video changed, someone's leader, no video playing - if(chan.currentMedia == null || - id != chan.currentMedia.id || + if(chan.media == null || + id != chan.media.id || chan.leader != null) { return; } - chan.currentMedia.currentTime += (new Date().getTime() - chan.time) / 1000.0; + chan.media.currentTime += (new Date().getTime() - chan.time) / 1000.0; chan.time = new Date().getTime(); // Show's over, move on to the next thing - if(chan.currentMedia.currentTime > chan.currentMedia.seconds) { + if(chan.media.currentTime > chan.media.seconds) { chan.playNext(); } // Send updates about every 5 seconds else if(chan.i % 5 == 0) { - chan.sendAll("mediaUpdate", chan.currentMedia.packupdate()); + chan.sendAll("mediaUpdate", chan.media.packupdate()); } chan.i++; - setTimeout(function() { channelVideoUpdate(chan, id); }, 1000); + setTimeout(function() { mediaUpdate(chan, id); }, 1000); } Channel.prototype.enqueue = function(data) { @@ -534,7 +533,7 @@ Channel.prototype.enqueue = function(data) { }); break; case "tw": - var media = new Media(data.id, "Twitch ~ " + data.id, "--:--", "li"); + var media = new Media(data.id, "Twitch ~ " + data.id, "--:--", "tw"); this.queue.splice(idx, 0, media); this.sendAll("queue", { media: media.pack(), @@ -730,10 +729,10 @@ Channel.prototype.move = function(data) { }); // Account for moving things around the active video - if(data.src < this.position && data.dest >= this.currentPosition) { + if(data.src < this.position && data.dest >= this.position) { this.position--; } - else if(data.src > this.position && data.dest < this.currentPosition) { + else if(data.src > this.position && data.dest < this.position) { this.position++ } else if(data.src == this.position) { diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 6193c201..1035592b 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -208,18 +208,13 @@ function initCallbacks() { $("#currenttitle").text("Currently Playing: " + data.title); if(data.type != "sc" && MEDIATYPE == "sc") fixSoundcloudShit(); - if(data.type == "yt") - updateYT(data); - else if(data.type == "tw") - loadTwitch(data.id); - else if(data.type == "li") - loadLivestream(data.id); - else if(data.type == "sc") - updateSC(data); - else if(data.type == "vi") - updateVI(data); - else if(data.type == "dm") - updateDM(data); + if(data.type != MEDIATYPE) { + MEDIATYPE = data.type; + PLAYER = new Media(data); + } + else { + PLAYER.update(data); + } }); socket.on("userlist", function(data) { @@ -239,6 +234,16 @@ function initCallbacks() { if(LEADER) { // I'm a leader! Set up sync function sendVideoUpdate = function() { + PLAYER.getTime(function(seconds) { + socket.emit("mediaUpdate", { + id: PLAYER.id, + seconds: seconds, + paused: false, + type: PLAYER.type + }); + }); + + /* if(MEDIATYPE == "yt") { socket.emit("mediaUpdate", { id: parseYTURL(PLAYER.getVideoUrl()), @@ -275,6 +280,7 @@ function initCallbacks() { type: "dm" }); } + */ }; } // I'm not a leader. Don"t send syncs to the server diff --git a/www/assets/js/client.js b/www/assets/js/client.js index 9d6bc0d0..1bd8e686 100644 --- a/www/assets/js/client.js +++ b/www/assets/js/client.js @@ -102,17 +102,6 @@ tag.src = "http://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName("script")[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); -// Load the Dailymotion iframe API - -/* -var tag = document.createElement("script"); -tag.src = "http://api.dmcdn.net/all.js"; -var firstScriptTag = document.getElementsByTagName("script")[0]; -firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); -*/ - - - if(uname != null && pw != null && pw != "false") { socket.emit("login", { name: uname, @@ -391,45 +380,7 @@ function synchtubeLayout() { } function onYouTubeIframeAPIReady() { - PLAYER = new YT.Player("ytapiplayer", { - height: VHEIGHT, - width: VWIDTH, - videoId: "", - playerVars: { - "autoplay": 0, - "controls": 1, - }, - events: { - "onReady": onPlayerReady, - "onStateChange": onPlayerStateChange - } - }); -} - -function onPlayerReady() { - socket.emit("playerReady"); -} - -function onPlayerStateChange(state) { - if(LEADER && state.data == YT.PlayerState.ENDED) { - socket.emit("playNext"); - } - else if(LEADER && state.data == YT.PlayerState.PAUSED) { - socket.emit("mediaUpdate", { - id: parseYTURL(PLAYER.getVideoUrl()), - seconds: PLAYER.getCurrentTime(), - type: "yt", - paused: true - }); - } - if(LEADER && state.data == YT.PlayerState.PLAYING) { - socket.emit("mediaUpdate", { - id: parseYTURL(PLAYER.getVideoUrl()), - seconds: PLAYER.getCurrentTime(), - type: "yt", - paused: false - }); - } + PLAYER = new Media({id: "", type: "yt"}); } function createCookie(name,value,days) { diff --git a/www/assets/js/functions.js b/www/assets/js/functions.js index 93e02cc7..e162efd3 100644 --- a/www/assets/js/functions.js +++ b/www/assets/js/functions.js @@ -317,172 +317,6 @@ function moveVideo(src, dest, noanim) { POSITION++; } -// YouTube Synchronization -function updateYT(data) { - if(MEDIATYPE != "yt") { - removeCurrentPlayer(); - MEDIATYPE = "yt"; - // Note to Soundcloud/Vimeo API designers: - // YouTube"s API is actually nice to use - PLAYER = new YT.Player("ytapiplayer", { - height: VHEIGHT, - width: VWIDTH, - videoId: "", - playerVars: { - "autoplay": 0, - "controls": 1, - }, - events: { - "onReady": onPlayerReady, - "onStateChange": onPlayerStateChange - } - }); - } - // Load new video - if(PLAYER.getVideoUrl && data.id != parseYTURL(PLAYER.getVideoUrl())) { - PLAYER.loadVideoById(data.id, data.currentTime, $("#quality").val()); - if(data.paused) - PLAYER.pauseVideo(); - } - // Sync playback time - else if(PLAYER.seekTo) { - if(Math.abs(PLAYER.getCurrentTime() - data.currentTime) > SYNC_THRESHOLD) - PLAYER.seekTo(data.currentTime, true); - if(!data.paused) - PLAYER.playVideo(); - } -} - -// Soundcloud synchronization -function updateSC(data) { - if(MEDIATYPE != "sc") { - unfixSoundcloudShit(); - var currentEmbed = $("#ytapiplayer"); - var iframe = $("").insertBefore(currentEmbed); - currentEmbed.remove(); - iframe.attr("id","ytapiplayer"); - iframe.attr("src", "https://w.soundcloud.com/player/?url="); - iframe.css("width", "100%").attr("height", "166") - .attr("frameborder", "no"); - - PLAYER = SC.Widget("ytapiplayer"); - MEDIATYPE = "sc"; - } - // Server is on a different soundcloud track than client - if(PLAYER.mediaId != data.id) { - PLAYER.load(data.id, { - auto_play: true - }); - // Keep track of current ID - PLAYER.mediaId = data.id; - } - // Soundcloud"s API is async - // Query the playback position and compare that with the sync packet - PLAYER.getPosition(function(pos) { - if(Math.abs(pos / 1000 - data.currentTime) > SYNC_THRESHOLD) { - PLAYER.seekTo(data.currentTime * 1000); - } - }); -} - -// Dailymotion synchronization -function updateDM(data) { - if(MEDIATYPE != "dm") { - removeCurrentPlayer(); - PLAYER = DM.player("ytapiplayer", { - video: data.id, - width: parseInt(VWIDTH), - height: parseInt(VHEIGHT), - params: {autoplay: 1} - }); - - PLAYER.mediaId = data.id; - MEDIATYPE = "dm"; - } - else if(PLAYER.mediaId != data.id) { - PLAYER.api("load", data.id); - PLAYER.mediaId = data.id; - } - else { - if(Math.abs(data.currentTime - PLAYER.currentTime) > SYNC_THRESHOLD) { - PLAYER.api("seek", data.currentTime); - } - else if(PLAYER.currentTime > data.seconds || PLAYER.currentTime+"" == "NaN") { - PLAYER.api("load", data.id); - } - } -} - -// Vimeo synchronization -// URGH building a synchronizing tool is so frustrating when -// these APIs are designed to be async -function updateVI(data) { - if(MEDIATYPE != "vi") { - initVI(data); - } - // Either vimeo"s API doesn"t support loading a new video - // or their terrible documentation doesn"t document it - else if(data.id != PLAYER.videoid) { - initVI(data); - } - - PLAYER.api("getCurrentTime", function(time) { - if(Math.abs(time - data.currentTime) > SYNC_THRESHOLD) { - PLAYER.api("seekTo", data.currentTime); - } - }); -} - -// Loads up a Vimeo player -function initVI(data) { - var currentEmbed = $("#ytapiplayer"); - var div = currentEmbed.parent(); - currentEmbed.remove(); - // Ugly but it"s the only way I managed to get the API calls to work - div[0].innerHTML += ""; - // $f() is defined by froogaloop, Vimeo"s API wrapper - PLAYER = $f($("iframe")[0]); - // So we can retrieve the ID synchronously instead of waiting for - // getVideoId with a callback - PLAYER.videoid = data.id; - PLAYER.addEvent("ready", function() { - // Autoplay - PLAYER.api("play"); - }); - MEDIATYPE = "vi"; -} - -function loadTwitch(channel) { - MEDIATYPE = "tw"; - - removeCurrentPlayer(); - var url = "http://www.twitch.tv/widgets/live_embed_player.swf?channel="+channel; - var params = { - allowFullScreen:"true", - allowScriptAccess:"always", - allowNetworking:"all", - movie:"http://www.twitch.tv/widgets/live_embed_player.swf", - id: "live_embed_player_flash", - flashvars:"hostname=www.twitch.tv&channel="+channel+"&auto_play=true&start_volume=100" - }; - swfobject.embedSWF( url, "ytapiplayer", VWIDTH, VHEIGHT, "8", null, null, params, {} ); -} - -function loadLivestream(channel) { - MEDIATYPE = "li"; - removeCurrentPlayer(); - flashvars = { channel: channel }; - params = { AllowScriptAccess: "always" }; - swfobject.embedSWF("http://cdn.livestream.com/chromelessPlayer/v20/playerapi.swf", "ytapiplayer", VWIDTH, VHEIGHT, "9.0.0", "expressInstall.swf", flashvars, params); -} - -function removeCurrentPlayer(){ - var currentEmbed = $("#ytapiplayer"); - var placeholder = $("
").insertBefore(currentEmbed); - currentEmbed.remove(); - placeholder.attr("id","ytapiplayer"); -} - function parseVideoURL(url){ url = url.trim() if(typeof(url) != "string") diff --git a/www/assets/js/media.js b/www/assets/js/media.js new file mode 100644 index 00000000..ad6db78f --- /dev/null +++ b/www/assets/js/media.js @@ -0,0 +1,243 @@ +var Media = function(data) { + this.id = data.id; + this.type = data.type; + + switch(this.type) { + case "yt": + this.initYouTube(); + break; + case "vi": + this.initVimeo(); + break; + case "dm": + this.initDailymotion(); + break; + case "sc": + this.initSoundcloud(); + break; + case "li": + this.initLivestream(); + break; + case "tw": + this.initTwitch(); + break; + default: + break; + } +} + +Media.prototype.initYouTube = function() { + this.removeOld(); + this.player = new YT.Player("ytapiplayer", { + height: VHEIGHT, + width: VWIDTH, + videoId: this.id, + playerVars: { + "autoplay": 1, + "controls": 1, + }, + events: { + onPlayerReady: function() { + socket.emit("playerReady"); + } + } + }); + + this.load = function(data) { + this.player.loadVideoById(data.id, data.currentTime); + this.id = data.id; + } + + this.pause = function() { + this.player.pauseVideo(); + } + + this.play = function() { + this.player.playVideo(); + } + + this.getTime = function(callback) { + callback(this.player.getCurrentTime()); + } + + this.seek = function(time) { + this.player.seekTo(time, true); + } +} + +Media.prototype.initVimeo = function() { + + var iframe = $("").insertBefore($("#ytapiplayer")); + $("#ytapiplayer").remove(); + iframe.attr("id", "ytapiplayer"); + iframe.attr("width", VWIDTH); + iframe.attr("height", VHEIGHT); + iframe.attr("src", "http://player.vimeo.com/video/"+this.id+"?api=1&player_id=ytapiplayer"); + iframe.attr("webkitAllowFullScreen", ""); + iframe.attr("mozallowfullscreen", ""); + iframe.attr("allowFullScreen", ""); + iframe.css("border", "none"); + + this.player = $f(iframe[0]); + $f(iframe[0]).addEvent("ready", function() { + this.player = $f(iframe[0]); + this.play(); + }.bind(this)); + + this.load = function(data) { + this.id = data.id; + this.initVimeo(); + } + + this.pause = function() { + this.player.api("pause"); + } + + this.play = function() { + this.player.api("play"); + } + + this.getTime = function(callback) { + this.player.api("getCurrentTime", callback); + } + + this.seek = function(time) { + this.player.api("seekTo", time); + } +} + +Media.prototype.initDailymotion = function() { + this.removeOld(); + this.player = DM.player("ytapiplayer", { + video: this.id, + width: parseInt(VWIDTH), + height: parseInt(VHEIGHT), + params: {autoplay: 1} + }); + + this.load = function(data) { + this.id = data.id; + this.player.api("load", data.id); + } + + this.pause = function() { + this.player.api("pause"); + } + + this.play = function() { + this.player.api("play"); + } + + this.getTime = function(callback) { + callback(this.player.currentTime); + } + + this.seek = function(seconds) { + this.player.api("seek", seconds); + } +} + +Media.prototype.initSoundcloud = function() { + unfixSoundcloudShit(); + var iframe = $("").insertBefore($("#ytapiplayer")); + $("#ytapiplayer").remove(); + + iframe.attr("id", "ytapiplayer"); + iframe.attr("src", "https://w.soundcloud.com/player/?url=" + this.id); + iframe.css("width", "100%").attr("height", "166"); + iframe.css("border", "none"); + + this.player = SC.Widget("ytapiplayer"); + setTimeout(function() { this.play(); }.bind(this), 1000); + + this.load = function(data) { + this.id = data.id; + this.player.load(data.id, {auto_play: true}); + } + + this.pause = function() { + this.player.pause(); + } + + this.play = function() { + this.player.play(); + } + + this.getTime = function(callback) { + this.player.getPosition(function(pos) { + callback(pos / 1000); + }); + } + + this.seek = function(seconds) { + this.player.seekTo(seconds * 1000); + } +} + +Media.prototype.initLivestream = function() { + this.removeOld(); + var flashvars = {channel: this.id}; + var params = {AllowScriptAccess: "always"}; + swfobject.embedSWF("http://cdn.livestream.com/chromelessPlayer/v20/playerapi.swf", "ytapiplayer", VWIDTH, VHEIGHT, "9.0.0", "expressInstall.swf", flashvars, params); + + this.load = function(data) { + this.id = data.id; + this.initLivestream(); + } + + this.pause = function() { } + + this.play = function() { } + + this.getTime = function() { } + + this.seek = function() { } +} + +Media.prototype.initTwitch = function() { + this.removeOld(); + var url = "http://www.twitch.tv/widgets/live_embed_player.swf?channel="+this.id; + var params = { + allowFullScreen:"true", + allowScriptAccess:"always", + allowNetworking:"all", + movie:"http://www.twitch.tv/widgets/live_embed_player.swf", + id: "live_embed_player_flash", + flashvars:"hostname=www.twitch.tv&channel="+this.id+"&auto_play=true&start_volume=100" + }; + swfobject.embedSWF( url, "ytapiplayer", VWIDTH, VHEIGHT, "8", null, null, params, {} ); + + this.load = function(data) { + this.id = data.id; + this.initTwitch(); + } + + this.pause = function() { } + + this.play = function() { } + + this.getTime = function() { } + + this.seek = function() { } +} + +Media.prototype.update = function(data) { + if(data.id != this.id) { + this.load(data); + } + if(data.paused) { + this.pause(); + } + this.getTime(function(seconds) { + if(Math.abs(data.currentTime - seconds) > SYNC_THRESHOLD) { + this.seek(data.currentTime); + } + }.bind(this)); +} + +Media.prototype.removeOld = function() { + var old = $("#ytapiplayer"); + var placeholder = $("").insertBefore(old); + old.remove(); + placeholder.attr("id", "ytapiplayer"); +} diff --git a/www/index.html b/www/index.html index 309ad6f2..6b2c9cf5 100644 --- a/www/index.html +++ b/www/index.html @@ -208,6 +208,7 @@ +