diff --git a/docs/testing.md b/docs/testing.md index 3bcd748..05292c0 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -28,6 +28,8 @@ This document describes the process involved in testing the extension. - For each type of playlist - [ ] Visit playlist page + - [ ] Verify extension loaded + - There should be a log in the browser console with the text `Loaded.` - [ ] Verify the following - [ ] A summary section is displayed within the playlist information panel located on the left-hand side of the page @@ -60,3 +62,5 @@ This document describes the process involved in testing the extension. summary section - [ ] Selecting the "Show unavailable videos" settings triggers a recalulation of the playlist duration +- For non-playlist pages, after 15 seconds have elapsed there should be a yellow + warning log in the browser console with the text `Could not find a playlist.` diff --git a/package.json b/package.json index e392f53..fd06ac6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An extension to calculate & display the total duration of a youtube playlist.", "author": "nrednav", "private": true, - "version": "2.1.1", + "version": "2.1.2", "type": "module", "engines": { "node": ">=20", diff --git a/src/main.js b/src/main.js index fe1d2bd..f72bd86 100644 --- a/src/main.js +++ b/src/main.js @@ -1,11 +1,14 @@ import { main } from "src/modules/index"; import "./main.css"; +import { logger } from "./shared/modules/logger"; // Entry-point if (document.readyState !== "loading") { + logger.info("Loaded."); main(); } else { document.addEventListener("DOMContentLoaded", () => { + logger.info("Loaded."); main(); }); } diff --git a/src/modules/index.js b/src/modules/index.js index 8e3771a..18ff697 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -4,6 +4,7 @@ import { convertSecondsToTimestamp, getTimestampFromVideo } from "src/shared/modules/timestamp"; +import { logger } from "src/shared/modules/logger"; const main = () => { checkPlaylistReady(); @@ -19,11 +20,13 @@ const checkPlaylistReady = () => { if (pollCount >= maxPollCount) clearInterval(playlistPoll); if (pollCount > 15 && window.location.pathname !== "/playlist") { + logger.warn("Could not find a playlist."); clearInterval(playlistPoll); return; } if ( + document.querySelector(elementSelectors.playlist) && document.querySelector(elementSelectors.timestamp) && countUnavailableTimestamps() === countUnavailableVideos() ) { @@ -72,8 +75,14 @@ const countUnavailableTimestamps = () => { .filter((timestamp) => timestamp === null).length; }; +/** + * Returns a list of video elements found within the playlist element + * @returns {Element[]} + **/ const getVideos = () => { const playlistElement = document.querySelector(elementSelectors.playlist); + if (!playlistElement) return []; + const videos = playlistElement.getElementsByTagName(elementSelectors.video); return [...videos]; }; @@ -151,6 +160,12 @@ const setupPage = () => { }; const onYoutubeNavigationFinished = () => { + document.removeEventListener( + "yt-navigate-finish", + onYoutubeNavigationFinished, + false + ); + window.ytpdc.playlistObserver?.disconnect(); window.ytpdc = { diff --git a/src/shared/modules/logger.js b/src/shared/modules/logger.js new file mode 100644 index 0000000..ab943e4 --- /dev/null +++ b/src/shared/modules/logger.js @@ -0,0 +1,23 @@ +class Logger { + static instance; + + constructor() { + const { version } = chrome.runtime.getManifest(); + this.prefix = `YTPDC (v${version}):`; + } + + static getInstance() { + return Logger.instance ? Logger.instance : new Logger(); + } + + logWithPrefix(logMethod) { + return (...args) => logMethod(this.prefix, ...args); + } + + info = this.logWithPrefix(console.info); + debug = this.logWithPrefix(console.debug); + warn = this.logWithPrefix(console.warn); + error = this.logWithPrefix(console.error); +} + +export const logger = Logger.getInstance(); diff --git a/src/shared/modules/timestamp.js b/src/shared/modules/timestamp.js index bdb241e..ba7d78b 100644 --- a/src/shared/modules/timestamp.js +++ b/src/shared/modules/timestamp.js @@ -53,12 +53,18 @@ export const getTimestampFromVideo = (video) => { const timestamp = timestampElement.innerText; if (!timestamp) return null; + const sanitizedTimestamp = timestamp.trim().replace(/\n/g, ""); + + // Does the timetamp match hh:mm:ss? // Ref: Timestamp regex from https://stackoverflow.com/a/8318367 - const timestampSanitized = timestamp - .trim() - .replace(/\n/g, "") - .match(/((?:(?:([01]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d))/)[0]; + const matches = sanitizedTimestamp.match( + /((?:(?:([01]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d))/ + ); - const timestampAsSeconds = convertTimestampToSeconds(timestampSanitized); - return timestampAsSeconds; + if (matches) { + return convertTimestampToSeconds(matches[0]); + } else { + // Timestamp exists but does not match hh:mm:ss, treat it as 0 seconds + return 0; + } };