diff --git a/CHANGELOG.md b/CHANGELOG.md index 2138554c..5d2323c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog -## v3.1.3-beta +## v3.1.3 +- When ignoring or only shuffling from shorts, the button will now more accurately display how long the shuffle will take. - Cleaned up the popup and moved some settings to a separate menu. -- Fixed a bug where a correction of database corruption would reduce the user's daily API quota. +- Fixed a bug where fixing a rare database corruption issue would reduce the user's daily API quota. ## v3.1.2 diff --git a/package-lock.json b/package-lock.json index 2b68f132..3140cc1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "random-youtube-video", - "version": "3.1.1", + "version": "3.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "random-youtube-video", - "version": "3.1.1", + "version": "3.1.3", "dependencies": { "@babel/runtime": "^7.18.6", "firebase": "^9.22.0" diff --git a/package.json b/package.json index bbfd0bc3..3a01f908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "random-youtube-video", - "version": "3.1.2", + "version": "3.1.3", "description": "Customize, shuffle and play random videos from any YouTube channel.", "scripts": { "dev": "concurrently \"npm run dev:chromium\" \"npm run dev:firefox\"", diff --git a/src/chromeStorage.js b/src/chromeStorage.js index d30f7e74..92f75233 100644 --- a/src/chromeStorage.js +++ b/src/chromeStorage.js @@ -42,8 +42,7 @@ export async function getUserQuotaRemainingToday() { return configSync.userQuotaRemainingToday; } -// -- Private -- -async function validateConfigSync() { +export async function validateConfigSync() { const configSyncValues = await chrome.storage.sync.get(); // Set default values for config values that do not exist in sync storage diff --git a/src/shuffleVideo.js b/src/shuffleVideo.js index 9bad4f35..0c3b157f 100644 --- a/src/shuffleVideo.js +++ b/src/shuffleVideo.js @@ -24,10 +24,10 @@ export async function chooseRandomVideo(channelId, firedFromPopup, progressTextE try { // While chooseRandomVideo is running, we need to keep the service worker alive // Otherwise, it will get stopped after 30 seconds and we will get an error if fetching the videos takes longer - // So every 25 seconds, we send a message to the service worker to keep it alive + // So every 20 seconds, we send a message to the service worker to keep it alive var keepServiceWorkerAlive = setInterval(() => { chrome.runtime.sendMessage({ command: "connectionTest" }); - }, 25000); + }, 20000); /* c8 ignore stop */ // Each user has a set amount of quota they can use per day. @@ -123,7 +123,7 @@ export async function chooseRandomVideo(channelId, firedFromPopup, progressTextE let chosenVideos; var encounteredDeletedVideos; - ({ chosenVideos, playlistInfo, shouldUpdateDatabase, encounteredDeletedVideos } = await chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpdateDatabase)); + ({ chosenVideos, playlistInfo, shouldUpdateDatabase, encounteredDeletedVideos } = await chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpdateDatabase, progressTextElement)); // Save the playlist to the database and locally playlistInfo = await handlePlaylistDatabaseUpload(playlistInfo, uploadsPlaylistId, shouldUpdateDatabase, databaseSharing, encounteredDeletedVideos); @@ -674,7 +674,7 @@ async function isShort(videoId) { return videoIsShort; } -// Requests the API key from the background script +// Requests an API key from the background script async function getAPIKey(useAPIKeyAtIndex = null) { const msg = { command: "getAPIKey", @@ -701,7 +701,7 @@ async function getAPIKey(useAPIKeyAtIndex = null) { return { APIKey, isCustomKey, keyIndex }; } -async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpdateDatabase) { +async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpdateDatabase, progressTextElement) { let activeShuffleFilterOption = configSync.channelSettings[channelId]?.activeOption ?? "allVideosOption"; let activeOptionValue; @@ -763,16 +763,21 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd console.log(`Choosing ${numVideosToChoose} random video(s).`); + // We keep track of the progress of determining the video types if there is a shorts handling filter, to display on the button + let numVideosProcessed = 0; + const initialTotalNumVideos = videosToShuffle.length; + // We use this label to break out of both the for loop and the while loop if there are no more videos after encountering a deleted video outerLoop: for (let i = 0; i < numVideosToChoose; i++) { if (videosToShuffle.length === 0) { - // All available videos were chosen from, so we need to terminate the loop early + // All available videos were chosen, so we need to terminate the loop early console.log(`No more videos to choose from (${numVideosToChoose - i} videos too few uploaded on channel).`); break outerLoop; } randomVideo = videosToShuffle[Math.floor(Math.random() * videosToShuffle.length)]; + numVideosProcessed++; // If the video does not exist, remove it from the playlist and choose a new one, until we find one that exists if (!await testVideoExistence(randomVideo, allVideos[randomVideo])) { @@ -786,6 +791,7 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd // Remove the deleted video from the videosToShuffle array and choose a new random video videosToShuffle.splice(videosToShuffle.indexOf(randomVideo), 1); randomVideo = videosToShuffle[Math.floor(Math.random() * videosToShuffle.length)]; + numVideosProcessed++; console.log(`The chosen video does not exist anymore, so it will be removed from the database. A new random video has been chosen: ${randomVideo}`); @@ -818,8 +824,11 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd if (playlistInfo["videos"]["unknownType"][randomVideo] !== undefined) { const videoIsShort = await isShort(randomVideo); - // What follows is dependent on if the video is a short or not, and the user's settings + // We display either the percentage of videos processed or the percentage of videos chosen (vs. needed), whichever is higher + const percentage = Math.max(Math.round(chosenVideos.length / numVideosToChoose * 100), Math.round(numVideosProcessed / initialTotalNumVideos * 100)); + updateProgressTextElement(progressTextElement, `\xa0Sorting: ${percentage}%`, `${percentage}%`); + // What follows is dependent on if the video is a short or not, and the user's settings // Case 1: !isShort && ignoreShorts => Success if (!videoIsShort && configSync.shuffleIgnoreShortsOption == "2") { // Move the video to the knownVideos subdictionary diff --git a/static/manifest.json b/static/manifest.json index 6495dad5..de093560 100644 --- a/static/manifest.json +++ b/static/manifest.json @@ -1,8 +1,8 @@ { "name": "Random YouTube Video", "description": "Customize, shuffle and play random videos from any YouTube channel.", - "version": "3.1.2", - "version_name": "3.1.3-beta", + "version": "3.1.3", + "version_name": "3.1.3", "manifest_version": 3, "content_scripts": [ { diff --git a/test/chromeStorage.test.js b/test/chromeStorage.test.js index 61fda253..29ca63bf 100644 --- a/test/chromeStorage.test.js +++ b/test/chromeStorage.test.js @@ -1,6 +1,6 @@ import expect from 'expect.js'; -import { configSync, setSyncStorageValue, removeSyncStorageValue, getUserQuotaRemainingToday } from '../src/chromeStorage.js'; +import { configSync, setSyncStorageValue, removeSyncStorageValue, getUserQuotaRemainingToday, validateConfigSync } from '../src/chromeStorage.js'; import { configSyncDefaults } from '../src/config.js'; describe('chromeStorage', function () { @@ -75,7 +75,6 @@ describe('chromeStorage', function () { }); context('removeSyncStorageValue()', function () { - it('should remove the value from the configSync object', async function () { await setSyncStorageValue("testKey4", "testValue4"); @@ -86,10 +85,9 @@ describe('chromeStorage', function () { expect(configSync).to.not.have.key("testKey4"); }); - }); - context('getUserQuotaRemainingToday()', function () { + context('getUserQuotaRemainingToday()', function () { it('should return the number of requests the user can still make to the Youtube API today', async function () { await setSyncStorageValue("userQuotaRemainingToday", 20); @@ -117,7 +115,69 @@ describe('chromeStorage', function () { // Check that the reset time is set to midnight expect(configSync.userQuotaResetTime).to.be(new Date(new Date().setHours(24, 0, 0, 0)).getTime()); }); + }); + + context('validateConfigSync()', function () { + context('fix incorrect settings', async function () { + it('should reset the useCustomApiKeyOption if no API key is set', async function () { + await setSyncStorageValue("useCustomApiKeyOption", true); + await setSyncStorageValue("customYoutubeApiKey", null); + + await validateConfigSync(); + + expect(configSync.useCustomApiKeyOption).to.be(false); + }); + + it('should enable database sharing if no API key is set', async function () { + await setSyncStorageValue("useCustomApiKeyOption", false); + await setSyncStorageValue("databaseSharingEnabledOption", false); + + await validateConfigSync(); + + expect(configSync.databaseSharingEnabledOption).to.be(true); + }); + + it('should disable reusing the new tab if shuffling does not open a new tab', async function () { + await setSyncStorageValue("shuffleOpenInNewTabOption", false); + await setSyncStorageValue("shuffleReUseNewTabOption", true); + + await validateConfigSync(); + expect(configSync.shuffleReUseNewTabOption).to.be(false); + }); + + it('should ensure valid values are set for ignoring shorts (0-2)', async function () { + // Too small + await setSyncStorageValue("shuffleIgnoreShortsOption", -1); + + await validateConfigSync(); + + expect(configSync.shuffleIgnoreShortsOption).to.be(1); + + // Too large + await setSyncStorageValue("shuffleIgnoreShortsOption", 3); + + await validateConfigSync(); + + expect(configSync.shuffleIgnoreShortsOption).to.be(1); + }); + + it('should ensure valid values are set for the number of videos in a playlist', async function () { + // Too small + await setSyncStorageValue("shuffleNumVideosInPlaylist", 0); + + await validateConfigSync(); + + expect(configSync.shuffleNumVideosInPlaylist).to.be(10); + + // Too large + await setSyncStorageValue("shuffleNumVideosInPlaylist", 51); + + await validateConfigSync(); + + expect(configSync.shuffleNumVideosInPlaylist).to.be(10); + }); + }); }); }); \ No newline at end of file