瀏覽代碼

Added partymode queue with option to remove song, fixed lots of issues with timings, YT, etc. Added modal to add songs to queue.

KrisVos130 8 年之前
父節點
當前提交
07797808d3

+ 2 - 0
app/client/head.html

@@ -44,6 +44,8 @@
             Session.set("YTLoaded", true);
         }
 
+        var YT = null;
+
         // Google Analytics Initialising
         (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
                     (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

+ 51 - 0
app/client/scripts/events.js

@@ -1640,6 +1640,23 @@ Template.communityStation.events({
             }
         })
     },
+    "click #add-song-to-queue-search-button": function () {
+        var songs = [];
+        Session.set("songResultsQueue", songs);
+        $.ajax({
+            type: "GET",
+            url: "https://www.googleapis.com/youtube/v3/search?part=snippet&q=" + $("#add-song-to-queue-search").val() + "&key=AIzaSyAgBdacEWrHCHVPPM4k-AFM7uXg-Q__YXY&type=video&maxResults=15",
+            applicationType: "application/json",
+            contentType: "json",
+            success: function (data) {
+                for (var i in data.items) {
+                    var item = data.items[i];
+                    songs.push({title: item.snippet.title, artist: item.snippet.channelTitle, id: item.id.videoId, image: item.snippet.thumbnails.medium.url});
+                }
+                Session.set("songResultsQueue", songs);
+            }
+        })
+    },
     "click #logout": function() {
         Meteor.logout();
     },
@@ -1858,6 +1875,25 @@ Template.communityStation.events({
             }
         });
     },
+    "click #clear-queue-search": function() {
+        Session.set("songResultsQueue", []);
+    },
+    "click .queue-item-remove": function(e) {
+        var id = $(e.target).attr("data-id");
+        if (id === undefined) {
+            id = $(e.target).parent().attr("data-id");
+        }
+        Meteor.call("removeIdFromCommunityStationQueue", Session.get("CommunityStationName"), id, function(err, res) {
+            console.log(111, err, res);
+            if (err) {
+                var $toastContent = $('<span><strong>Song not removed.</strong> ' + err.reason + '</span>');
+                Materialize.toast($toastContent, 2000);
+            } else {
+                var $toastContent = $('<span><strong>Song removed.</strong></span>');
+                Materialize.toast($toastContent, 2000);
+            }
+        });
+    },
     "click .edit-playlist-button": function(e) {
         if ($(e.target).hasClass("edit-playlist-button")) {
             Session.set("editingPlaylistName", $(e.target).data("playlist"));
@@ -1908,6 +1944,21 @@ Template.communityStation.events({
         });
         $("#add_playlist_video").val("");
     },
+    "click .addSongQueue": function(e) {
+        var id = $(e.target).attr("data-result");
+        Meteor.call("addSongToCommunityStationQueue", Session.get("CommunityStationName"), id, function(err) {
+            if (err) {
+                console.log(err);
+                var $toastContent = $('<span><strong>Video not added to queue.</strong> ' + err.reason + '</span>');
+                Materialize.toast($toastContent, 2000);
+            } else {
+                var $toastContent = $('<span><strong>Video added to queue.</strong></span>');
+                Materialize.toast($toastContent, 2000);
+                $("#add-song-to-queue-search").val("");
+                $("#add-song-to-queue").closeModal();
+            }
+        });
+    },
     "click .playlistSongRemove": function(e) {
         var id = $(e.target).data("id");
         if (id === undefined) {

+ 46 - 4
app/client/scripts/helpers.js

@@ -556,6 +556,18 @@ Template.room.helpers({
 });
 
 Template.communityStation.helpers({
+    getUsername: function(id) {
+        return Meteor.users.findOne(id).profile.username;
+    },
+    queue: function() {
+        var name = Session.get("CommunityStationName");
+        var room = CommunityStations.findOne({name: name});
+        if (room !== undefined && room.partyModeEnabled === true) {
+            return room.queue;
+        } else {
+            return [];
+        }
+    },
     noCurrentSong: function() {
         return Session.get("noCurrentSong");
     },
@@ -575,18 +587,30 @@ Template.communityStation.helpers({
             return "";
         }
     },
-    partyMode: function() {
+    partyModeEnabled: function() {
         var name = Session.get("CommunityStationName");
         var room = CommunityStations.findOne({name: name});
-        if (room.partyModeEnabled === true) {
+        if (room !== undefined && room.partyModeEnabled === true) {
             return true;
         } else {
             return false;
         }
     },
+    partyModeEnabledHidden: function() {
+        var name = Session.get("CommunityStationName");
+        var room = CommunityStations.findOne({name: name});
+        if (room !== undefined && room.partyModeEnabled === true) {
+            return "";
+        } else {
+            return "hidden";
+        }
+    },
     singleVideoResults: function() {
         return Session.get("songResults");
     },
+    singleVideoResultsQueue: function() {
+        return Session.get("songResultsQueue");
+    },
     singleVideoResultsActive: function() {
         var songs = Session.get("songResults");
         if (songs !== undefined && songs.length > 0) {
@@ -595,6 +619,14 @@ Template.communityStation.helpers({
             return false;
         }
     },
+    singleVideoResultsActiveQueue: function() {
+        var songs = Session.get("songResultsQueue");
+        if (songs !== undefined && songs.length > 0) {
+            return true;
+        } else {
+            return false;
+        }
+    },
     hasMoreThanOne: function(array) {
         if (array.length > 1) {
             return true;
@@ -678,7 +710,12 @@ Template.communityStation.helpers({
         return Session.get("loaded");
     },
     paused: function () {
-        return Session.get("state") === "paused";
+        var room = CommunityStations.findOne({name: Session.get("CommunityStationName")});
+        if (room !== undefined) {
+            return room.state === "paused";
+        } else {
+            return false;
+        }
     },
     private: function () {
         var room = CommunityStations.findOne({name: Session.get("CommunityStationName")});
@@ -689,7 +726,12 @@ Template.communityStation.helpers({
         }
     },
     playing: function() {
-        return Session.get("state") === "playing";
+        var room = CommunityStations.findOne({name: Session.get("CommunityStationName")});
+        if (room !== undefined) {
+            return room.state === "playing";
+        } else {
+            return false;
+        }
     },
     currentSong: function(){
         return Session.get("currentSong");

+ 4 - 2
app/client/scripts/onCreated.js

@@ -202,7 +202,7 @@ Template.room.onCreated(function () {
                     }, 500));
                 } else {
                     if (YTPlayer === undefined) {
-                        if (YT !== undefined && YT.Player !== undefined) {
+                        if (YT !== undefined && YT !== null && YT.Player !== undefined) {
                             YTPlayer = new YT.Player("player", {
                                 height: 270,
                                 width: 480,
@@ -308,6 +308,8 @@ Template.room.onCreated(function () {
                         var song_duration = currentSong.duration;
                         if (song_duration <= duration) {
                             Session.set("pauseVideo", true);
+                        } else if (Session.get("pauseVideo") === true) {
+                            Session.set("pauseVideo", false);
                         }
                         var d = moment.duration(duration, 'seconds');
                         if (Session.get("state") === "playing") {
@@ -444,7 +446,7 @@ Template.communityStation.onCreated(function () {
                     }, 500));
                 } else {
                     if (YTPlayer === undefined) {
-                        if (YT !== undefined && YT.Player !== undefined) {
+                        if (YT !== undefined && YT !== null && YT.Player !== undefined) {
                             YTPlayer = new YT.Player("player", {
                                 height: 270,
                                 width: 480,

+ 43 - 1
app/client/stylesheets/app.css

@@ -313,6 +313,18 @@ input[type="range"] + .thumb {
     vertical-align: middle;
 }
 
+.queue-title {
+    height: 18px !important;
+    line-height: 1.5rem !important;
+    padding: 0 !important;
+}
+
+.queue-icon {
+    position: absolute !important;
+    right: 0 !important;
+    margin-top: 12px !important;
+}
+
 #sidenav-overlay{
     display: none
 }
@@ -383,7 +395,37 @@ input[type="range"] + .thumb {
     overflow-y: scroll;
 }
 
-.pl-item{
+.queue-item {
+    padding-top: 1px !important;
+}
+
+.queue-item:first-child {
+    margin-top: 10px;
+}
+
+.queue-item-title {
+    margin: 0;
+    font-size: 1.3em;
+    line-height: 25px;
+}
+
+.queue-item-username {
+    margin: 0;
+    font-size: 1.1em;
+    line-height: 15px;
+}
+
+.queue-item-remove {
+
+}
+
+.queue-item-text {
+    padding-top: 10px;
+    padding-bottom: 10px;
+    width: 90%;
+}
+
+.pl-item {
     pointer-events: none;
 }
 

+ 66 - 4
app/client/templates/communityStation.html

@@ -29,7 +29,7 @@
                 <span class="brand-logo center">{{communityStationDisplayName}}
                     <small>(by {{communityStationOwnerName}})</small></span>
                 <ul class="right hide-on-med-and-down">
-                    <!--li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Playlist" id="playlist-slideout" data-activates="playlist-slide-out" class="tooltipped header-collapse"><i class="material-icons">queue_music</i></a></li-->
+                    <li class="{{partyModeEnabledHidden}}"><a href="#" data-position="bottom" data-delay="50" data-tooltip="Queue" id="playlist-slideout" data-activates="queue-slide-out" class="tooltipped header-collapse"><i class="material-icons">queue_music</i></a></li>
                     {{#if isLoggedIn}}
                         <li><a href="#" data-position="bottom" data-delay="50" data-tooltip="Chat" id="chat-slideout"
                                data-activates="chat-slide-out" class="tooltipped header-collapse"><i
@@ -116,10 +116,27 @@
         </div>
     </div>
     <!--Playlist slideout-->
-    <!--div id="playlist-slide-out" class="side-nav room-slideout">
-        <h5>Playlist</h5>
+    <div id="queue-slide-out" class="side-nav room-slideout">
+        <h5>Queue</h5>
+
+        <ul id="queue-ul" style="max-height: calc(100% - 105px); overflow-y: auto; margin: 0;">
+            {{#each song in queue}}
+                <li class="queue-item" style="clear: both">
+                    <a class="queue-item-remove" style="float:right; height: 0; width: 0;" href="#" data-id={{song.song.id}}><i class="material-icons" data-id={{song.song.id}}>clear</i></a>
+                    <div class="queue-item-text">
+                        <p class="queue-item-title">{{song.song.title}}</p>
+                        <p class="queue-item-username">Added by <a style="display: inline-block; height: 10px; padding: 0; color: #039be5;" href="/profile/{{getUsernameFromId song.requestedBy}}">{{getUsernameFromId song.requestedBy}}</a></p>
+                    </div>
+                    <hr>
+                </li>
+            {{/each}}
+        </ul>
+        <a id="add-song-to-queue-button" class="waves-effect waves-light btn musare white-text" href="#add-song-to-queue">Add song to queue</a>
+    </div>
+
+
+
 
-    </div-->
     <div id="users-slide-out" class="side-nav room-slideout">
         <h5>Users In Room</h5>
         <ul style="margin-bottom: 64px;">
@@ -298,6 +315,45 @@
             <button class="btn waves-effect waves-light left red" id="delete_playlist">Delete playlist</button>
         </div>
     </div>
+
+    <!-- Edit Playlist Modal -->
+    <div id="add-song-to-queue" class="modal">
+        <div class="modal-content">
+            <h4>Add song to queue</h4>
+            <div class="input-field">
+                <input id="add-song-to-queue-search" type="text" class="validate">
+                <label for="add-song-to-queue-search">Search for song to add</label>
+            </div>
+            <a class="waves-effect waves-light btn" id="add-song-to-queue-search-button"><i class="material-icons left">search</i>Search</a>
+            <a class="waves-effect waves-light btn orange" id="clear-queue-search"><i class="material-icons left">delete</i>Clear results</a>
+        </div>
+        {{#if singleVideoResultsActiveQueue}}
+            <div id="single-video-results-queue">
+                <div style="overflow: auto; height: 400px; margin-top: 1rem;">
+                    <ul class="collection light-blue-text">
+                        {{#each result in singleVideoResultsQueue}}
+                            <li class="collection-item avatar youtube-search-result-li">
+                                <img src="{{result.image}}" onerror="this.src='/notes.png'" alt=""
+                                     class="video-import-thumbnail">
+                                <span class="title video-import-text">{{result.title}}</span>
+                                <p class="video-import-text">{{result.artist}} <br>
+                                    <a href="https://youtube.com/watch?v={{result.id}}" target="_blank">View Video
+                                        In YouTube</a>
+                                </p>
+                                <a href="#" class="secondary-content addSongQueue" data-result="{{result.id}}"><i
+                                        class="material-icons" data-result="{{result.id}}">add</i></a>
+                            </li>
+                        {{/each}}
+                    </ul>
+                </div>
+            </div>
+        {{/if}}
+        <div class="divider"></div>
+        <div class="modal-footer">
+            <a class="modal-action modal-close waves-effect btn">Close</a>
+        </div>
+    </div>
+
     <!-- Create Playlist Modal -->
     <div id="create_playlist_modal" class="modal">
         <div class="modal-content">
@@ -318,6 +374,12 @@
         </div>
     </div>
     <script>
+        $("#add-song-to-queue-button").leanModal({
+            dismissible: true,
+            opacity: .5,
+            in_duration: 500,
+            out_duration: 200
+        });
         $(".edit-playlist-button").leanModal({
             dismissible: true,
             opacity: .5,

+ 91 - 8
app/server/server.js

@@ -471,7 +471,7 @@ function CommunityStation(name) {
                             currentSong: {
                                 song: queue[0].song,
                                 requestedBy: queue[0].requestedBy,
-                                started: startedAt
+                                started: Date.now()
                             }
                         }
                     });
@@ -486,6 +486,8 @@ function CommunityStation(name) {
                         }
                     });
                 }
+            } else {
+                CommunityStations.update({name: name}, {$set: {currentSong: {}}});
             }
         } else {
             playlist = PrivatePlaylists.findOne({name: _room.playlist, owner: _room.owner});
@@ -512,7 +514,7 @@ function CommunityStation(name) {
                 $set: {
                     currentSong: {
                         song: songs[currentSong],
-                        started: startedAt
+                        started: Date.now()
                     }
                 }
             });
@@ -527,19 +529,51 @@ function CommunityStation(name) {
     this.songTimer = function () {
         if (state !== "paused") {
             startedAt = Date.now();
-
             if (timer !== undefined) {
                 timer.pause();
             }
             timerInitialised = true;
             if (this.isPartyModeEnabled()) {
+                if (timer !== undefined) {
+                    timer.resetTimeWhenPaused();
+                }
+                timer = new Timer(function () {
+                    self.skipSong();
+                }, queue[0].song.duration * 1000);
+                timerInitialised = true;
+            } else {
+                if (timer !== undefined) {
+                    timer.resetTimeWhenPaused();
+                }
+                timer = new Timer(function () {
+                    self.skipSong();
+                }, songs[currentSong].duration * 1000);
+                timerInitialised = true;
+            }
+        } else {
+            startedAt = Date.now();
+            if (timer !== undefined) {
+                timer.pause();
+            }
+            timerInitialised = true;
+            if (this.isPartyModeEnabled()) {
+                if (timer !== undefined) {
+                    timer.resetTimeWhenPaused();
+                }
                 timer = new Timer(function () {
                     self.skipSong();
                 }, queue[0].song.duration * 1000);
+                timerInitialised = true;
+                timer.pause();
             } else {
+                if (timer !== undefined) {
+                    timer.resetTimeWhenPaused();
+                }
                 timer = new Timer(function () {
                     self.skipSong();
                 }, songs[currentSong].duration * 1000);
+                timerInitialised = true;
+                timer.pause();
             }
         }
     };
@@ -557,17 +591,27 @@ function CommunityStation(name) {
     };
     this.resumeRoom = function () {
         if (state !== "playing") {
+            if (this.isPartyModeEnabled()) {
+                var station = CommunityStations.findOne({name: name});
+                queue = station.queue;
+                if (queue.length === 0) {
+                    CommunityStations.update({name: name}, {$set: {currentSong: {}}});
+                }
+            }
+
             if (!timerInitialised) {
                 if (this.isPartyModeEnabled()) {
                     if (queue.length > 0) {
                         timer = new Timer(function () {
                             self.skipSong();
                         }, queue[0].song.duration * 1000);
+                        timerInitialised = true;
                     }
                 } else {
                     timer = new Timer(function () {
                         self.skipSong();
                     }, songs[currentSong].duration * 1000);
+                    timerInitialised = true;
                 }
             }
             if (timer !== undefined) {
@@ -667,6 +711,28 @@ function CommunityStation(name) {
         }
     };
 
+    this.removeSongFromQueue = function(id) {
+        var exists = false;
+        var obj;
+        var station = CommunityStations.findOne({name: name});
+        queue = station.queue;
+        queue.forEach(function(song) {
+            if (song.song.id === id) {
+                obj = song;
+                exists = true;
+            }
+        });
+        if (exists) {
+            CommunityStations.update({name: name}, {$pull: {queue: obj}});
+            if (station.currentSong.song.id === id) {
+                this.skipSong();
+            }
+            return true;
+        } else {
+            throw new Meteor.Error(500, "This song is not in the queue.");
+        }
+    };
+
     this.skipSong();
     this.voted = [];
 }
@@ -693,19 +759,23 @@ function shuffle(array) {
 function Timer(callback, delay) {
     var timerId, start, remaining = delay;
     var timeWhenPaused = 0;
-    var timePaused = new Date();
+    var timePaused = Date.now();
 
     this.pause = function () {
         Meteor.clearTimeout(timerId);
-        remaining -= new Date() - start;
-        timePaused = new Date();
+        remaining -= Date.now() - start;
+        timePaused = Date.now();
     };
 
     this.resume = function () {
-        start = new Date();
+        start = Date.now();
         Meteor.clearTimeout(timerId);
         timerId = Meteor.setTimeout(callback, remaining);
-        timeWhenPaused += new Date() - timePaused;
+        timeWhenPaused += Date.now() - timePaused;
+    };
+
+    this.resetTimeWhenPaused = function() {
+        timeWhenPaused = 0;
     };
 
     this.timeWhenPaused = function () {
@@ -1625,6 +1695,19 @@ Meteor.updatedMethods({
             throw new Meteor.Error(403, "Invalid permissions.");
         }
     },
+    removeIdFromCommunityStationQueue: function (name, id) {
+        if (isCommunityStationOwner(name) && !isBanned()) {
+            getCommunityStation(name, function (station) {
+                if (station === undefined) {
+                    throw new Meteor.Error(403, "Room doesn't exist.");
+                } else {
+                    station.removeSongFromQueue(id);
+                }
+            });
+        } else {
+            throw new Meteor.Error(403, "Invalid permissions.");
+        }
+    },
     addAllowedToCommunityStation: function (name, allowed) {
         if (isCommunityStationOwner(name) && !isBanned()) {
             getCommunityStation(name, function (station) {