From 844fc898d46ed8afb0da879ccb7b9ad84549e353 Mon Sep 17 00:00:00 2001 From: phixyn Date: Thu, 22 Oct 2020 20:24:53 +0100 Subject: [PATCH 1/8] Client: Implement a function to extract video ID from a YouTube URL Implements `getVideoId(youtubeUrl)`, which takes in a string containing a YouTube URL and tries to extract a video ID from it. This uses the querystring parser. If no querystring is found in the URL, it splits the URL's path by a '/' and checks if the last part of the URL is a valid YouTube ID. This handles URLs like 'https://youtu.be/id'. A function to check if a string is a valid HTTP or HTTPS URL has also been added for convenience. --- static/js/app.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/static/js/app.js b/static/js/app.js index e710d04..62ef566 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -3,6 +3,7 @@ $(document).foundation(); // Add your websocket server IP address here const websocket = new WebSocket("ws://:14670"); +const VIDEO_ID_LENGTH = 11; var player; var state; @@ -460,3 +461,63 @@ function updateSliderAndInputAttributes(newStartTime, newEndTime) { */ endTimeInput.val(state.end.toString()).change(); } + +/** + * TODO + */ +function isValidHttpUrl(urlString) { + let urlObj; + + try { + urlObj = new URL(urlString); + } catch (err) { + console.error(`[ERROR] ${err.name}: ${err.message}`); + return false; + } + + return urlObj.protocol === "http:" || urlObj.protocol === "https:"; +} + +/** + * TODO + */ +function getVideoId(youtubeUrl) { + console.debug(`getVideoId(${youtubeUrl}) result:`); + let videoId; + let urlObj; + + try { + urlObj = new URL(youtubeUrl); + } catch (err) { + // TODO show error toast to user + console.error(`[ERROR] ${err.name} : ${err.message}`); + return null; + } + + // Check if there is a querystring in the URL and parse it + if (urlObj.search !== "") { + let qsParse = Qs.parse(urlObj.search, { ignoreQueryPrefix: true }); + if (qsParse.hasOwnProperty("v") && qsParse.v !== "") { + videoId = qsParse.v; + } else { + // TODO show error toast to user + console.error("[ERROR] Could not get video ID from YouTube URL."); + return null; + } + } else if (urlObj.pathname !== "") { + // Handle 'youtu.be/id' and 'youtube.com/embed/id' + let pathArray = urlObj.pathname.split("/"); + videoId = pathArray[pathArray.length - 1]; + } + + // Validate video ID by checking the length + if (videoId.length === VIDEO_ID_LENGTH) { + console.debug(videoId); + return videoId; + } else { + // TODO show error toast to user + console.error(`[ERROR] Invalid video ID in URL: '${youtubeUrl}'`); + console.debug(`[DEBUG] Got unexpected length in ID: '${videoId}'`); + return null; + } +} From 4c4a11bcaf1f1c5659862b98d677737bbc84c8b1 Mon Sep 17 00:00:00 2001 From: phixyn Date: Thu, 22 Oct 2020 20:31:09 +0100 Subject: [PATCH 2/8] Client: Allow users to enter a YouTube video URL to load a new video Refactor updatePlayer() to handle YouTube video URLs in addition to IDs. Makes use of the previously implemented functions (see parent commit) to check if the string in the input field is a URL. For video IDs, an additional check has been put in place to somewhat validate the ID (just by checking its length for now). If the URL does not contain a video ID, or if the video ID is invalid, then the function returns to exit, and nothing is sent to the WebSocket server. However, errors are only logged, not shown to the user yet. --- static/js/app.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/static/js/app.js b/static/js/app.js index 62ef566..b3e3be4 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -281,7 +281,30 @@ function onPlayerStateChange(event) { */ function updatePlayer() { console.debug("[DEBUG] Updating player (state.v, state.start)."); - state.v = videoIdInput.val(); + + let videoInputVal = videoIdInput.val(); + + if (isValidHttpUrl(videoInputVal)) { + let videoId = getVideoId(videoInputVal); + if (videoId !== null) { + state.v = videoId; + } else { + // TODO show error toast to user + console.error( + `[ERROR] Invalid video URL or ID in input: '${videoInputVal}'` + ); + return; + } + } else if (videoInputVal.length === VIDEO_ID_LENGTH) { + state.v = videoInputVal; + } else { + // TODO show error toast to user + console.error( + `[ERROR] Invalid video URL or ID in input: '${videoInputVal}'` + ); + return; + } + console.log("[INFO] Loading new video in player..."); /* loadPlaylist() and setLoop() are required to make infinite loops of full * videos (i.e. not portions of a video). It's for this same reason that we From cf14bc0beb9fc00c391a9acf960142c1eb701443 Mon Sep 17 00:00:00 2001 From: phixyn Date: Thu, 22 Oct 2020 20:34:48 +0100 Subject: [PATCH 3/8] Looper: Show users that they can use a video URL to load a new video Update "Video ID" string to "Video URL or ID". --- static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/index.html b/static/index.html index 5e14c82..45d1e53 100644 --- a/static/index.html +++ b/static/index.html @@ -65,7 +65,7 @@
-