From c4549740db7e4982edb68034dbebc511e555fa97 Mon Sep 17 00:00:00 2001 From: Praveen Raj Date: Mon, 23 Oct 2023 18:30:02 +0530 Subject: [PATCH] Added autoplay next related video feature --- css/styles.css | 2 +- index.html | 3 + js/everything.js | 141 ++++++++++++++++++++++++++++++++++++++++++++++- js/zap-common.js | 1 - 4 files changed, 143 insertions(+), 4 deletions(-) diff --git a/css/styles.css b/css/styles.css index 9cac9e26..cea19f1a 100644 --- a/css/styles.css +++ b/css/styles.css @@ -100,7 +100,7 @@ a { @media all and (min-width: 480px) { #container { - max-width: 450px; + max-width: 470px; } } diff --git a/index.html b/index.html index 9b8203d0..bcadc19d 100644 --- a/index.html +++ b/index.html @@ -88,6 +88,9 @@

+ diff --git a/js/everything.js b/js/everything.js index d0d5568c..f8d5d8e9 100644 --- a/js/everything.js +++ b/js/everything.js @@ -1,7 +1,8 @@ /* global gtag, URI, getSearchResults, getAutocompleteSuggestions, parseYoutubeVideoID, getYouTubeVideoDescription */ var keyCodes = { - SPACEBAR: 32 + SPACEBAR: 32, + ENTER: 13 }; var timeIntervals = { @@ -15,6 +16,12 @@ var plyrPlayer; var youTubeDataApiKey = "AIzaSyCxVxsC5k46b8I-CLXlF3cZHjpiqP_myVk"; var currentVideoID; +// global playlist, this is populated with an ajax call +var tags = []; +var playList = new Set(); +var autoplayState = false; +const MAX_TAGS = 10; + var errorMessage = { init: function() { // nothing for now @@ -137,6 +144,7 @@ var ZenPlayer = { that.setupTitle(); that.setupVideoDescription(videoID); that.setupPlyrToggle(); + that.setupAutoplayToggle(); }); plyrPlayer.addEventListener("playing", function() { @@ -165,6 +173,17 @@ var ZenPlayer = { updateTweetMessage(); }); + // when player has finished playing + plyrPlayer.addEventListener("ended", function() { + if (autoplayState) { + if (playList.length === 0 || playList.size === 0) { + fetchSuggestedVideoIds(); + } + var newId = getNewVideoID(); + that.playNext(newId); + } + }); + plyrPlayer.addEventListener("timeupdate", function() { // Nothing is playing if (!plyrPlayer.plyr || !plyrPlayer.plyr.embed) { @@ -223,6 +242,11 @@ var ZenPlayer = { }); } }, + // play next song from autoplay + playNext: function(videoID) { + $("#v").val(videoID); + $("#form").submit(); + }, show: function() { $("#audioplayer").show(); // Hide the demo link as some video is playing @@ -258,6 +282,24 @@ var ZenPlayer = { toggleElement(event, ".plyr__video-wrapper", "Player"); }); }, + setupAutoplayToggle: function() { + // toggle auto next song playing + $("#toggleAutoplay").click(function(event) { + var toggleTextElement = $("#" + event.currentTarget.id); + if (autoplayState) { + toggleTextElement.text("Start autoplay"); + autoplayState = false; + window.sessionStorage.removeItem("autoPlay"); + window.sessionStorage.removeItem("playList"); + } + else { + toggleTextElement.text("Stop autoplay"); + autoplayState = true; + window.sessionStorage.setItem("autoPlay", true); + } + }); + }, + getVideoDescription: function(videoID) { var description = ""; @@ -275,6 +317,7 @@ var ZenPlayer = { } else { description = data.items[0].snippet.description; + tags = data.items[0].snippet.tags; } }, function(jqXHR, textStatus, errorThrown) { @@ -524,6 +567,81 @@ function pickDemo() { return demos[Math.floor(Math.random() * demos.length)]; } +function updateAutoplayToggle(state) { + if (state) { + $("#toggleAutoplay").text("Stop autoplay"); + } + else { + $("#toggleAutoplay").text("Start autoplay"); + } +} + +function getNewVideoID() { + var nextID = null; + nextID = playList.pop(); + while (currentVideoID === nextID) { + nextID = playList.pop(); + } + window.sessionStorage.setItem("playList", JSON.stringify(playList)); + return nextID; +} + +function fetchSuggestedVideoIds() { + if (playList.length === 0 || playList.size === 0) { + if (tags.length) { + // get similar videos, populate playList + if (!isFileProtocol()) { + for (let index = 0; index < tags.length && index < MAX_TAGS; index++) { + $.ajax({ + url: "https://www.googleapis.com/youtube/v3/search", + dataType: "json", + async: false, + data: { + key: youTubeDataApiKey, + part: "snippet", + type: "video", + order: "relevance", + q: tags[index], + maxResults: 2 + }, + success: onRelatedVideoFetchSuccess + }).fail(function(jqXHR, textStatus, errorThrown) { + logError(jqXHR, textStatus, errorThrown, "Related video lookup error"); + }); + } + playList = Array.from(playList); + window.sessionStorage.setItem("playList", JSON.stringify(playList)); + } + } + } +} + +function onRelatedVideoFetchSuccess(data) { + // push items into playlist + for (var i = 0; i < data.items.length; i++) { + playList.add(data.items[i].id.videoId); + } +} + +function loadAutoPlayDetails() { + // load playList from session storage on reload + if (window.sessionStorage.getItem("playList")) { + playList = JSON.parse(window.sessionStorage.getItem("playList")); + } + + // fetch autoPlay from session storage on reload + if (window.sessionStorage.getItem("autoPlay")) { + autoplayState = window.sessionStorage.getItem("autoPlay"); + updateAutoplayToggle(autoplayState); + } +} + +function resetAutoPlayList() { + playList = new Set(); + tags = []; + window.sessionStorage.removeItem("playList"); +} + $(function() { if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { $("#container").hide(); @@ -534,6 +652,8 @@ $(function() { errorMessage.init(); + loadAutoPlayDetails(); + // How do we know if the value is truly invalid? // Preload the form from the URL var currentVideoID = getCurrentVideoID(); @@ -608,7 +728,6 @@ $(function() { data: { key: youTubeDataApiKey, part: "snippet", - fields: "items/snippet/description", id: videoID }, success: function(data) { @@ -616,12 +735,15 @@ $(function() { window.location.href = makeSearchURL(formValue); } else { + tags = data.items[0].snippet.tags; window.location.href = makeListenURL(videoID, formValueTime); } } }).fail(function(jqXHR, textStatus, errorThrown) { logError(jqXHR, textStatus, errorThrown, "Lookup error"); }); + // fetching next videoIds for auto play + fetchSuggestedVideoIds(); } } else { @@ -654,6 +776,8 @@ $(function() { // Handle demo link click $("#demo").click(function(event) { event.preventDefault(); + resetAutoPlayList(); + gtag("send", "event", "demo", "clicked"); // Don't continue appending to the URL if it appears "good enough". @@ -670,13 +794,22 @@ $(function() { // Handle focus link click $("#focus-btn").click(function(event) { event.preventDefault(); + resetAutoPlayList(); + gtag("send", "event", "focus", "clicked"); // Redirect to the favorite "focus" URL window.location.href = makeListenURL(focusId); }); + // handle click on search icon + $("#submit").click(function() { + resetAutoPlayList(); + }); + // Check if the current ID is the focus ID $(window).on("load", function() { + loadAutoPlayDetails(); + // Show Focus Button if (window.location.href.indexOf(focusId) === -1) { $("#focus-btn").show(); @@ -709,5 +842,9 @@ $(function() { if (evt.keyCode === keyCodes.SPACEBAR && !$("#v").is(":focus")) { evt.preventDefault(); } + + if (evt.keyCode === keyCodes.ENTER && $("#v").is(":focus")) { + resetAutoPlayList(); + } }); }); diff --git a/js/zap-common.js b/js/zap-common.js index 9dc45d27..046ca1b4 100644 --- a/js/zap-common.js +++ b/js/zap-common.js @@ -27,7 +27,6 @@ function getYouTubeVideoDescription(videoID, youTubeDataApiKey, onSuccess, onFai data: { key: youTubeDataApiKey, part: "snippet", - fields: "items/snippet/description", id: videoID }, success: onSuccess