Bläddra i källkod

Rewrote queues system.

KrisVos130 9 år sedan
förälder
incheckning
a5a264f7ab

+ 146 - 59
app/client/scripts/events.js

@@ -371,7 +371,19 @@ Template.profile.events({
     }
 });
 
+var seekerBarInterval = undefined;
+
 Template.queues.events({
+    /* TODO Add undo delete button */
+    "input #id": function() {
+        console.log("Change!");
+        $("#previewPlayerContainer").addClass("hide-preview");
+    },
+    "input #img": function() {
+        var url = $("#img").val();
+        console.log(url);
+        Session.set("image_url", url);
+    },
     "click .preview-button": function(e){
         Session.set("song", this);
         $("#previewModal").openModal();
@@ -392,85 +404,153 @@ Template.queues.events({
         $("#dislikes").val(this.dislikes).change();
         $("#duration").val(this.duration).change();
         $("#skip-duration").val(this.skipDuration).change();
-        $("#editModal").openModal();
+        $("#previewPlayerContainer").addClass("hide-preview");
+        Session.set("image_url", this.img);
+        Session.set("editing", true);
+        $("#editModal").openModal({
+            complete : function() {
+                Session.set("editing", false);
+                if (YTPlayer !== undefined && YTPlayer.stopVideo !== undefined) {
+                    YTPlayer.stopVideo();
+                }
+            }
+        });
     },
     "click .add-song-button": function(e){
         var genre = $(e.target).data("genre") || $(e.target).parent().data("genre");
-        Meteor.call("addSongToPlaylist", genre, this);
+        Meteor.call("addSongToPlaylist", genre, this, function(err) {
+            console.log(err);
+            if (err) {
+                var $toastContent = $('<span><strong>Song not added.</strong> ' + err.reason + '</span>');
+                Materialize.toast($toastContent, 8000);
+            }
+        });
     },
     "click .deny-song-button": function(e){
         var genre = $(e.target).data("genre") || $(e.target).parent().data("genre");
         Meteor.call("removeSongFromQueue", genre, this.mid);
     },
     "click #play": function() {
-        $("#play").attr("disabled", true);
-        $("#stop").attr("disabled", false);
+        $("#previewPlayerContainer").removeClass("hide-preview");
         var song = Session.get("song");
         var id = song.id;
-        var type = song.type;
         var volume = localStorage.getItem("volume") || 20;
-
-        if (YTPlayer === undefined) {
-            YTPlayer = new YT.Player("previewPlayer", {
-                height: 540,
-                width: 568,
-                videoId: id,
-                playerVars: {autoplay: 1, controls: 0, iv_load_policy: 3, showinfo: 0},
-                events: {
-                    'onReady': function(event) {
-                        event.target.seekTo(Number(song.skipDuration));
-                        event.target.playVideo();
-                        event.target.setVolume(volume);
-                    },
-                    'onStateChange': function(event){
-                        if (event.data == YT.PlayerState.PAUSED) {
+        if (song.duration !== 0) {
+            $("#play").attr("disabled", true);
+            $("#stop").attr("disabled", false);
+            $("#pause").attr("disabled", false);
+            $("#forward").attr("disabled", false);
+            if (YTPlayer === undefined) {
+                YTPlayer = new YT.Player("previewPlayer", {
+                    height: 540,
+                    width: 568,
+                    videoId: id,
+                    playerVars: {autoplay: 1, controls: 0, iv_load_policy: 3, showinfo: 0, fs: 0},
+                    events: {
+                        'onReady': function(event) {
+                            event.target.seekTo(Number(song.skipDuration));
                             event.target.playVideo();
-                        }
-                        if (event.data == YT.PlayerState.PLAYING) {
-                            $("#play").attr("disabled", true);
-                            $("#stop").attr("disabled", false);
-                        } else {
-                            $("#play").attr("disabled", false);
-                            $("#stop").attr("disabled", true);
+                            event.target.setVolume(volume);
+                            var duration = Session.get("song").duration;
+                            var d = moment.duration(parseInt(duration), 'seconds');
+                            $("#time-total").text(d.minutes() + ":" + ("0" + d.seconds()).slice(-2));
+                        },
+                        'onStateChange': function(event){
+                            if (event.data == YT.PlayerState.PAUSED) {
+                                if (seekerBarInterval !== undefined) {
+                                    Meteor.clearInterval(seekerBarInterval);
+                                    seekerBarInterval = undefined;
+                                }
+                            }
+                            if (event.data == YT.PlayerState.UNSTARTED) {
+                                if (seekerBarInterval !== undefined) {
+                                    Meteor.clearInterval(seekerBarInterval);
+                                    seekerBarInterval = undefined;
+                                }
+                                $(".seeker-bar").css({width: "0"});
+                                $("#time-elapsed").text("0:00");
+                                $("#previewPlayerContainer").addClass("hide-preview");
+                            }
+                            if (event.data == YT.PlayerState.PLAYING) {
+                                seekerBarInterval = Meteor.setInterval(function() {
+                                    var duration = Session.get("song").duration;
+                                    var timeElapsed = YTPlayer.getCurrentTime();
+                                    var skipDuration = Session.get("song").skipDuration;
+
+                                    if (duration <= (timeElapsed - skipDuration)) {
+                                        YTPlayer.stopVideo();
+                                        $("#play").attr("disabled", false);
+                                        $("#stop").attr("disabled", true);
+                                        $("#pause").attr("disabled", true);
+                                        $("#forward").attr("disabled", true);
+                                        $("#previewPlayerContainer").addClass("hide-preview");
+                                        $(".seeker-bar").css({width: "0"});
+                                        $("#time-elapsed").text("0:00");
+                                        Meteor.clearInterval(seekerBarInterval);
+                                    } else {
+                                        var percentComplete = (timeElapsed - skipDuration) / duration * 100;
+                                        $(".seeker-bar").css({width: percentComplete + "%"});
+                                        var d = moment.duration(timeElapsed - skipDuration, 'seconds');
+                                        $("#time-elapsed").text(d.minutes() + ":" + ("0" + d.seconds()).slice(-2));
+                                    }
+                                }, 100);
+                                $("#play").attr("disabled", true);
+                                $("#stop").attr("disabled", false);
+                                $("#pause").attr("disabled", false);
+                                $("#forward").attr("disabled", false);
+                            } else {
+                                $("#play").attr("disabled", false);
+                                $("#stop").attr("disabled", true);
+                                $("#pause").attr("disabled", true);
+                                $("#forward").attr("disabled", true);
+                            }
                         }
                     }
+                });
+            } else {
+                if (YTPlayer.getPlayerState() === 2) {
+                    YTPlayer.playVideo();
+                } else {
+                    console.log(id, song.skipDuration, song.duration);
+                    YTPlayer.loadVideoById(id);
+                    YTPlayer.seekTo(Number(song.skipDuration));
                 }
-            });
-        } else {
-            YTPlayer.loadVideoById(id);
-            YTPlayer.seekTo(Number(song.skipDuration));
-        }
-        $("#previewPlayer").show();
-
-        if (previewEndSongTimeout !== undefined) {
-            Meteor.clearTimeout(previewEndSongTimeout);
-        }
-        previewEndSongTimeout = Meteor.setTimeout(function() {
-            if (YTPlayer !== undefined) {
-                YTPlayer.stopVideo();
             }
-            $("#play").attr("disabled", false);
-            $("#stop").attr("disabled", true);
-            $("#previewPlayer").hide();
-        }, song.duration * 1000);
+            $("#previewPlayerContainer").removeClass("hide-preview");
+        }
     },
     "click #stop": function() {
         $("#play").attr("disabled", false);
         $("#stop").attr("disabled", true);
+        $("#pause").attr("disabled", true);
+        $("#forward").attr("disabled", true);
         if (previewEndSongTimeout !== undefined) {
             Meteor.clearTimeout(previewEndSongTimeout);
         }
-        if (YTPlayer !== undefined) {
+        if (YTPlayer !== undefined && YTPlayer.stopVideo !== undefined) {
             YTPlayer.stopVideo();
         }
     },
+    "click #pause": function() {
+        $("#play").attr("disabled", false);
+        $("#stop").attr("disabled", false);
+        $("#pause").attr("disabled", true);
+        $("#forward").attr("disabled", true);
+        if (previewEndSongTimeout !== undefined) {
+            Meteor.clearTimeout(previewEndSongTimeout);
+        }
+        if (YTPlayer !== undefined && YTPlayer.pauseVideo !== undefined) {
+            YTPlayer.pauseVideo();
+        }
+    },
     "click #forward": function() {
         var error = false;
         if (YTPlayer !== undefined) {
             var duration = Number(Session.get("song").duration) | 0;
             var skipDuration = Number(Session.get("song").skipDuration) | 0;
             if (YTPlayer.getDuration() < duration + skipDuration) {
-                alert("The duration of the YouTube video is smaller than the duration.");
+                var $toastContent = $('<span><strong>Error.</strong> The song duration is longer than the length of the video.</span>');
+                Materialize.toast($toastContent, 8000);
                 error = true;
             } else {
                 YTPlayer.seekTo(skipDuration + duration - 10);
@@ -486,7 +566,9 @@ Template.queues.events({
                 }
                 $("#play").attr("disabled", false);
                 $("#stop").attr("disabled", true);
-                $("#previewPlayer").hide();
+                $("#pause").attr("disabled", true);
+                $("#forward").attr("disabled", true);
+                $("#previewPlayerContainer").addClass("hide-preview");
             }, 10000);
         }
     },
@@ -497,8 +579,8 @@ Template.queues.events({
             for(var i in data){
                 for(var j in data[i].items){
                     if(search.indexOf(data[i].items[j].name) !== -1 && artistName.indexOf(data[i].items[j].artists[0].name) !== -1){
-                        $("#img").val(data[i].items[j].album.images[2].url);
-                        $("#duration").val(data[i].items[j].duration_ms / 1000);
+                        $("#img").val(data[i].items[j].album.images[2].url).change();
+                        $("#duration").val(data[i].items[j].duration_ms / 1000).change();
                         return;
                     }
                 }
@@ -507,6 +589,7 @@ Template.queues.events({
     },
     "click #save-song-button": function() {
         var newSong = {};
+        newSong.mid = $("#mid").val();
         newSong.id = $("#id").val();
         newSong.likes = Number($("#likes").val());
         newSong.dislikes = Number($("#dislikes").val());
@@ -515,18 +598,19 @@ Template.queues.events({
         newSong.img = $("#img").val();
         newSong.duration = Number($("#duration").val());
         newSong.skipDuration = $("#skip-duration").val();
+        newSong.requestedBy = Session.get("song").requestedBy;
         if(newSong.skipDuration === undefined){
             newSong.skipDuration = 0;
         }
-        if (Session.get("type") === "playlist") {
-            Meteor.call("updatePlaylistSong", Session.get("genre"), Session.get("song"), newSong, function() {
-                $('#editModal').modal('hide');
-            });
-        } else {
-            Meteor.call("updateQueueSong", Session.get("genre"), Session.get("song"), newSong, function() {
-                $('#editModal').modal('hide');
-            });
-        }
+        Meteor.call("updateQueueSong", Session.get("genre"), Session.get("song"), newSong, function(err, res) {
+            console.log(err, res);
+            if (err) {
+                var $toastContent = $('<span><strong>Song not saved.</strong> ' + err.reason + '</span>');
+                Materialize.toast($toastContent, 8000);
+            } else {
+                Session.set("song", newSong);
+            }
+        });
     }
 });
 
@@ -1345,6 +1429,8 @@ Template.settings.events({
     }
 });
 
+var previewEndSongTimeout = undefined;
+
 Template.stations.events({
     "click .preview-button": function(e){
         Session.set("song", this);
@@ -1465,10 +1551,11 @@ Template.stations.events({
     "click #stop": function() {
         $("#play").attr("disabled", false);
         $("#stop").attr("disabled", true);
+        $("#pause").attr("disabled", true);
         if (previewEndSongTimeout !== undefined) {
             Meteor.clearTimeout(previewEndSongTimeout);
         }
-        if (YTPlayer !== undefined) {
+        if (YTPlayer !== undefined && YTPlayer.stopVideo !== undefined) {
             YTPlayer.stopVideo();
         }
     },

+ 3 - 0
app/client/scripts/helpers.js

@@ -208,6 +208,9 @@ Template.queues.helpers({
             }
         });
         return queues;
+    },
+    song_image: function() {
+        return Session.get("image_url");
     }
 });
 

+ 8 - 0
app/client/scripts/onCreated.js

@@ -77,6 +77,14 @@ Template.queues.onCreated(function() {
     var firstScriptTag = document.getElementsByTagName('script')[0];
     firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
     YTPlayer = undefined;
+    $(document).keydown(function(evt){
+        if (evt.keyCode==83 && (evt.ctrlKey)){
+            evt.preventDefault();
+            if (Session.get("editing") === true) {
+                $("#save-song-button").click();
+            }
+        }
+    });
 });
 
 Template.register.onCreated(function() {

+ 7 - 17
app/client/scripts/onRendered.js

@@ -14,24 +14,14 @@ Template.queues.onRendered(function() {
         }
     });
     $(document).ready(function() {
-        function makeSlider(){
-            var slider = $("#volume-slider").slider();
-            var volume = localStorage.getItem("volume") || 20;
-            $("#volume-slider").slider("setValue", volume);
-            if (slider.length === 0) {
-                Meteor.setTimeout(function() {
-                    makeSlider();
-                }, 500);
-            } else {
-                slider.on("slide", function(val) {
-                    localStorage.setItem("volume", val.value);
-                    if (YTPlayer !== undefined) {
-                        YTPlayer.setVolume(val.value);
-                    }
-                });
+        var volume = localStorage.getItem("volume") || 20;
+        $("#volume_slider").val(volume).on("input", function() {
+            volume = Number($("#volume_slider").val());
+            localStorage.setItem("volume", volume);
+            if (YTPlayer !== undefined) {
+                YTPlayer.setVolume(volume);
             }
-        }
-        makeSlider();
+        });
     });
 });
 

+ 56 - 0
app/client/stylesheets/app.css

@@ -45,4 +45,60 @@ main {
 
 .contact-details .material-icons{
     float: left;
+}
+
+.musare {
+    background-color: rgb(107, 197, 164) !important;
+}
+
+.range-field > input[type="range"] {
+    border-color: #C2C0C2;
+    border-right: 0;
+    border-left: 0;
+}
+
+.table-right-td {
+     text-align: right;
+ }
+
+.table-right-th {
+    width: 1px;
+}
+
+.hide-preview {
+    padding-bottom: 0 !important;
+}
+
+#song-preview {
+    width: 210px;
+    height: 210px;
+    min-height: 210px;
+    min-width: 210px;
+    max-height: 210px;
+    max-width: 210px;
+}
+
+#previewPlayerContainer {
+    margin-bottom: 20px;
+}
+
+
+.seeker-bar {
+    top: 0;
+    left: 0;
+    bottom: 0;
+    position: absolute;
+}
+
+.seeker-bar-container {
+    position: relative;
+    height: 5px;
+    display: block;
+    width: 100%;
+    overflow: hidden;
+
+}
+
+#preview-time {
+    margin-top: -9px;
 }

+ 53 - 69
app/client/templates/queues.html

@@ -12,45 +12,30 @@
                             <tr>
                                 <th>Title</th>
                                 <th>Artist(s)</th>
-                                <th>Type</th>
                                 <th>Id</th>
-                                <th>Likes</th>
-                                <th>Dislikes</th>
-                                <th>Img</th>
-                                <th>Preview</th>
-                                <th>Edit</th>
-                                <th>Add</th>
-                                <th colspan="10">Remove</th>
+                                <th>Mid</th>
+                                <th class="table-right-th">Edit</th>
+                                <th class="table-right-th">Add</th>
+                                <th class="table-right-th">Remove</th>
                             </tr>
                             </thead>
                             <tbody>
                             {{#each songs}}
                                 <tr>
-                                    <th scope="row">{{title}}</th>
-                                    <td>{{artist}}</td>
-                                    <td>{{type}}</td>
-                                    <td>{{id}}</td>
-                                    <td>{{likes}}</td>
-                                    <td>{{dislikes}}</td>
-                                    <td>
-                                        <a class="waves-effect waves-light btn modal-trigger preview-modal-button"
-                                           href="#previewModal">Preview Modal</a>
-                                    </td>
-                                    <td>
-                                        <button class="btn preview-button preview-modal-button" data-toggle="modal"
-                                                data-target="#previewModal">Preview
-                                        </button>
-                                    </td>
-                                    <td>
+                                    <th align="left" scope="row">{{title}}</th>
+                                    <td align="left">{{artist}}</td>
+                                    <td align="left">{{id}}</td>
+                                    <td align="left">{{mid}}</td>
+                                    <td class="table-right-td">
                                         <button class="btn edit-queue-button" data-genre="{{../type}}"
                                                 data-toggle="modal" data-target="#editModal">Edit
                                         </button>
                                     </td>
-                                    <td>
+                                    <td class="table-right-td">
                                         <button class="btn green add-song-button" data-genre="{{../type}}"><i
                                                 class="material-icons">thumb_up</i></button>
                                     </td>
-                                    <td>
+                                    <td class="table-right-td">
                                         <button class="btn red deny-song-button" data-genre="{{../type}}"><i
                                                 class="material-icons">thumb_down</i></button>
                                     </td>
@@ -62,48 +47,44 @@
                 </div>
             {{/each}}
         </div>
-
-        <div id="previewModal" class="modal">
-            <div class="modal-content">
-                <h4>Preview</h4>
-                <div width="960" height="540" id="previewPlayer"></div>
-                <button id="play" title="Play video" class="btn btn-success"><i class="fa fa-play"></i></button>
-                <button id="stop" title="Stop video" class="btn btn-danger" disabled><i class="fa fa-stop"></i>
-                </button>
-                <button id="forward" title="Go to the last 10 seconds of the video" class="btn btn-primary"><i
-                        class="fa fa-fast-forward"></i></button>
-                <div id="volume-container-admin">
-                    <input type="text" id="volume-slider" class="span2" value="" data-slider-min="0"
-                           data-slider-max="100" data-slider-step="1" data-slider-value="50"
-                           data-slider-orientation="horizontal" data-slider-selection="after"
-                           data-slider-tooltip="hide">
-                </div>
-            </div>
-            <div class="modal-footer">
-                <a href="#!" class=" modal-action modal-close waves-effect waves-light btn-flat">Close</a>
-            </div>
-        </div>
     </div>
-    <!--div id="previewImageModal" class="modal modal-fixed-footer" role="dialog">
-        <div class="modal-dialog">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal">&times;</button>
-                    <h4 class="modal-title">Preview Image</h4>
-                </div>
-                <div class="modal-body">
-                    <img alt="Not loading" id="preview-image" height="210px" width="210px" src=""/>
+    <div id="editModal" class="modal modal-fixed-footer">
+        <div class="modal-content musare white-text">
+            <div class="row">
+                <h4 class="center-align">Video Preview</h4>
+                <div class="video-container" id="previewPlayerContainer">
+                    <div width="960" height="540" id="previewPlayer"></div>
                 </div>
-                <div class="modal-footer">
-                    <button id="close-modal" type="button" class="btn btn-default" data-dismiss="modal">Close
-                    </button>
+                <div class="seeker-bar-container col l10 m10 s10 white" id="preview-progress">
+                    <div class="seeker-bar teal" style="width: 0%"></div>
                 </div>
+                <span class="col l2 m2 s2 center" id="preview-time">
+                    <span id="time-elapsed">0:00</span> / <span id="time-total">0:00</span>
+                </span>
+                <button id="play" title="Play video" class="btn green col m1 s1 l1 offset-l3 offset-m3 offset-s3">
+                    <i class="material-icons">play_arrow</i>
+                </button>
+                <button id="stop" title="Stop video" class="btn red col m1 s1 l1" disabled>
+                    <i class="material-icons">stop</i>
+                </button>
+                <button id="pause" title="Pause video" class="btn orange col m1 s1 l1" disabled>
+                    <i class="material-icons">pause</i>
+                </button>
+                <button id="forward" title="Go to the last 10 seconds of the video" class="btn blue col m1 s1 l1" disabled>
+                    <i class="material-icons">fast_forward</i>
+                </button>
+                <form class="col m2 s2 l2" action="#">
+                    <p class="range-field" style="margin-top: 0">
+                        <input type="range" id="volume_slider" min="0" max="100" />
+                    </p>
+                </form>
             </div>
-        </div>
-    </div-->
-    <div id="editModal" class="modal modal-fixed-footer">
-        <div class="modal-content">
             <div class="row">
+                <h4 class="center-align">Image Preview</h4>
+                <img id="song-preview" onerror="this.src='http://static.boredpanda.com/blog/wp-content/uploads/2014/04/amazing-fox-photos-182.jpg'" class="center-block" src="{{song_image}}"/>
+            </div>
+            <div class="row">
+                <h4 class="center-align">Edit Info</h4>
                 <div class="input-field col l8 m8 s12 offset-l2 offset-m2">
                     <i class="material-icons prefix">vpn_key</i>
                     <label for="mid" class="white-text">Song MID</label>
@@ -127,12 +108,12 @@
                 <div class="input-field col l8 m8 s12 offset-l2 offset-m2">
                     <i class="material-icons prefix">timelapse</i>
                     <label for="title" class="white-text">Song Duration</label>
-                    <input class="validate" name="duration" id="duration" type="number" min="0"/>
+                    <input class="validate" name="duration" id="duration" type="number" step="any" min="0"/>
                 </div>
                 <div class="input-field col l8 m8 s12 offset-l2 offset-m2">
                     <i class="material-icons prefix">timer_off</i>
                     <label for="skip-duration" class="white-text">Skip Duration</label>
-                    <input class="validate" id="skip-duration" type="number" min="0"/>
+                    <input class="validate" id="skip-duration" type="number" step="any" min="0"/>
                 </div>
                 <div class="input-field col l8 m8 s12 offset-l2 offset-m2">
                     <i class="material-icons prefix">thumb_up</i>
@@ -150,11 +131,14 @@
                     <input class="validate" name="img" id="img" type="url"/>
                 </div>
             </div>
-            <button type="button" id="get-spotify-info" class="button">Get Spotify Data</button>
-            <button type="button" id="save-song-button" class="button">Save Changes</button>
+            <div class="row">
+                <button type="button" id="get-spotify-info" class="btn btn-large col l6 m6 s10 offset-l3 offset-m3 offset-s1 waves-effect waves-light">Get Spotify Data</button>
+                <button type="button" id="save-song-button" class="btn btn-large col l6 m6 s10 offset-l3 offset-m3 offset-s1 waves-effect waves-light">Save Changes</button>
+            </div>
+
         </div>
-        <div class="modal-footer">
-            <a href="#!" class="modal-action modal-close waves-effect waves-light btn-flat ">X</a>
+        <div class="modal-footer musare white-text">
+            <a href="#!" class="modal-action modal-close waves-effect waves-light btn-flat white">X</a>
         </div>
     </div>
 </template>

+ 10 - 3
app/server/server.js

@@ -985,8 +985,14 @@ Meteor.methods({
     updateQueueSong: function (genre, oldSong, newSong) {
         if (isModerator() && !isBanned()) {
             newSong.mid = oldSong.mid;
-            Queues.update({type: genre, "songs": oldSong}, {$set: {"songs.$": newSong}});
-            return true;
+            Queues.update({type: genre, "songs": oldSong}, {$set: {"songs.$": newSong}}, function(err) {
+                console.log(err);
+                if (err) {
+                    throw err.sanitizedError;
+                } else {
+                    return true;
+                }
+            });
         } else {
             throw new Meteor.Error(403, "Invalid permissions.");
         }
@@ -1034,13 +1040,14 @@ Meteor.methods({
         }
     },
     addSongToPlaylist: function (type, songData) {
+        console.log(songData);
         if (isModerator() && !isBanned()) {
             type = type.toLowerCase();
             if (Rooms.find({type: type}).count() === 1) {
                 if (Playlists.find({type: type}).count() === 0) {
                     Playlists.insert({type: type, songs: []});
                 }
-                var requiredProperties = ["type", "mid", "id", "title", "artist", "duration", "skipDuration", "img", "likes", "dislikes", "requestedBy"];
+                var requiredProperties = ["mid", "id", "title", "artist", "duration", "skipDuration", "img", "likes", "dislikes", "requestedBy"];
                 if (songData !== undefined && Object.keys(songData).length === requiredProperties.length) {
                     for (var property in requiredProperties) {
                         if (songData[requiredProperties[property]] === undefined) {