Skip to content

Commit

Permalink
Added option to shuffle only from shorts (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
NikkelM authored Nov 12, 2023
1 parent 45468a0 commit 3f7537a
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 274 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Changelog

## v2.3.0
## v2.4.0 (Unreleased)

<!--Releasenotes start-->
- Added a new option to shuffle only from shorts.
- Fixed bugs that would prevent some initialization logic to run whenever the extension is updated.
<!--Releasenotes end-->

## v2.3.0

- Shuffling when excluding shorts is now a lot quicker if you have shuffled from the channel before, as the extension will remember which videos are shorts and skip them automatically.
- Added an additional message to the shuffle button if shuffling takes a bit longer due to ignoring shorts.
- Fixed a rare data inconsistency bug occurring with specific database values.
<!--Releasenotes end-->

## v2.2.4

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Chrome Web Store:
<img src="https://img.shields.io/chrome-web-store/v/kijgnjhogkjodpakfmhgleobifempckf?label=version"
alt="Chrome web store version"></a>
<a href="https://chrome.google.com/webstore/detail/random-youtube-video/kijgnjhogkjodpakfmhgleobifempckf">
<img src="https://img.shields.io/chrome-web-store/rating/kijgnjhogkjodpakfmhgleobifempckf?label=rating"
<img src="https://img.shields.io/chrome-web-store/stars/kijgnjhogkjodpakfmhgleobifempckf?label=rating"
alt="Chrome web store rating"></a>
<a href="https://chrome.google.com/webstore/detail/random-youtube-video/kijgnjhogkjodpakfmhgleobifempckf">
<img src="https://img.shields.io/chrome-web-store/users/kijgnjhogkjodpakfmhgleobifempckf?label=users"
Expand All @@ -19,7 +19,7 @@ Firefox:
<img src="https://img.shields.io/amo/v/random-youtube-video?label=version"
alt="Firefox version"></a>
<a href="https://addons.mozilla.org/en-GB/firefox/addon/random-youtube-video/">
<img src="https://img.shields.io/amo/rating/random-youtube-video?label=rating"
<img src="https://img.shields.io/amo/stars/random-youtube-video?label=rating"
alt="Firefox rating"></a>
<a href="https://addons.mozilla.org/en-GB/firefox/addon/random-youtube-video/">
<img alt="Mozilla Add-on" src="https://img.shields.io/amo/users/random-youtube-video?label=users"
Expand Down
47 changes: 28 additions & 19 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
import { configSync, setSyncStorageValue } from "./chromeStorage.js";

// ---------- Initialization/Chrome event listeners ----------
// On Chrome startup, we make sure we are not using too much local storage
chrome.runtime.onStartup.addListener(async function () {

// Check whether a new version was installed
async function initExtension() {
const manifestData = chrome.runtime.getManifest();
if (configSync.previousVersion < manifestData.version) {
await handleExtensionUpdate(manifestData, configSync.previousVersion);
}

checkLocalStorageCapacity();
}
await initExtension();

// Make sure we are not using too much local storage
async function checkLocalStorageCapacity() {
// If over 90% of the storage quota for playlists is used, remove playlists that have not been accessed in a long time
const utilizedStorage = await chrome.storage.local.getBytesInUse();
const maxLocalStorage = chrome.storage.local.QUOTA_BYTES;
Expand Down Expand Up @@ -32,25 +44,11 @@ chrome.runtime.onStartup.addListener(async function () {
chrome.storage.local.remove(playlistId);
}
}
});

// Check whether a new version was installed
chrome.runtime.onInstalled.addListener(async function (details) {
const manifestData = chrome.runtime.getManifest();

if (details.reason == "update" && details.previousVersion !== manifestData.version) {
await handleExtensionUpdate(manifestData, details.previousVersion);
} else if (details.reason == "install") {
await handleExtensionInstall(manifestData);
}
});

async function handleExtensionInstall(manifestData) {
console.log(`Extension was installed (v${manifestData.version})`);
}

async function handleExtensionUpdate(manifestData, previousVersion) {
console.log(`Extension was updated to version v${manifestData.version}`);
await setSyncStorageValue("previousVersion", manifestData.version);

// Handle changes that may be specific to a certain version change
await handleVersionSpecificUpdates(previousVersion);
Expand All @@ -69,7 +67,18 @@ async function handleExtensionUpdate(manifestData, previousVersion) {
}

async function handleVersionSpecificUpdates(previousVersion) {
// v1.5.0 added renamed some keys in the channelSettings object
// v2.4.0 changed the data type for the shuffleIgnoreShortsOption from boolean to number
if (previousVersion < "2.4.0") {
console.log("Updating sync storage to v2.4.0 format...");
const syncStorageContents = await chrome.storage.sync.get();
if (syncStorageContents["shuffleIgnoreShortsOption"] == true) {
await setSyncStorageValue("shuffleIgnoreShortsOption", 2);
} else {
await setSyncStorageValue("shuffleIgnoreShortsOption", 1);
}
}

// v1.5.0 renamed some keys in the channelSettings object
if (previousVersion < "1.5.0") {
console.log("Updating channelSettings to v1.5.0 format...");

Expand All @@ -83,7 +92,7 @@ async function handleVersionSpecificUpdates(previousVersion) {
delete channelSetting["shufflePercentage"];
}
}
await chrome.storage.sync.set(configSyncValues);
await setSyncStorageValue(configSyncValues);
}

// v1.3.0 removed the "youtubeAPIKey" key from local storage, which was replaced by the "youtubeAPIKeys" key
Expand Down
6 changes: 5 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export const configSyncDefaults = {
// These properties influence the behavior of the "Shuffle" button
"shuffleOpenInNewTabOption": true,
"shuffleReUseNewTabOption": true,
"shuffleIgnoreShortsOption": false,
// 0 = only shorts, 1 = no option set (shorts are included), 2 = ignore shorts
"shuffleIgnoreShortsOption": 1,
"shuffleOpenAsPlaylistOption": true,
// How many random videos to add to a playlist
"shuffleNumVideosInPlaylist": 10,
Expand All @@ -34,6 +35,9 @@ export const configSyncDefaults = {
"lastViewedChangelogVersion": "0",
// For april fools: Will be the number of the year in which the user was last rickrolled (we only want to rickroll the user once per year)
"wasLastRickRolledInYear": "1970",
// Used when updating the extension
/* c8 ignore next */
"previousVersion": typeof chrome !== "undefined" ? chrome.runtime.getManifest().version : "0",
};

export const shufflingHints = [
Expand Down
2 changes: 1 addition & 1 deletion src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async function shuffleVideos() {
var hasBeenShuffled = false;
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Shuffling...", 1000, () => { return (shuffleButtonTextElement.innerText === "\xa0Shuffle" && !hasBeenShuffled); });
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Still on it...", 5000, () => { return (shuffleButtonTextElement.innerText === "\xa0Shuffling..." && !hasBeenShuffled); });
if (configSync.shuffleIgnoreShortsOption) {
if (configSync.shuffleIgnoreShortsOption != "1") {
setDOMTextWithDelay(shuffleButtonTextElement, "\xa0Sorting shorts...", 8000, () => { return (shuffleButtonTextElement.innerText === "\xa0Still on it..." && !hasBeenShuffled); });
}

Expand Down
16 changes: 8 additions & 8 deletions src/html/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ function getPopupDomElements() {
shuffleOpenInNewTabOptionToggle: document.getElementById("shuffleOpenInNewTabOptionToggle"),
// Shuffling: Reuse tab option toggle
shuffleReUseNewTabOptionToggle: document.getElementById("shuffleReUseNewTabOptionToggle"),
// Shuffling : Ignore shorts option toggle
shuffleIgnoreShortsOptionToggle: document.getElementById("shuffleIgnoreShortsOptionToggle"),
// Shuffling : Ignore shorts option dropdown
shuffleIgnoreShortsOptionDropdown: document.getElementById("shuffleIgnoreShortsOptionDropdown"),
// Shuffling: Open as playlist option toggle
shuffleOpenAsPlaylistOptionToggle: document.getElementById("shuffleOpenAsPlaylistOptionToggle"),
// Shuffling: Number of videos in playlist div
Expand Down Expand Up @@ -117,8 +117,8 @@ async function setPopupDomElementValuesFromConfig(domElements) {
// If this option is enabled depends on the state of the shuffleOpenInNewTabOptionToggle
manageDependents(domElements, domElements.shuffleOpenInNewTabOptionToggle, configSync.shuffleOpenInNewTabOption);

// ----- Shuffling: Ignore shorts option toggle -----
domElements.shuffleIgnoreShortsOptionToggle.checked = configSync.shuffleIgnoreShortsOption;
// ----- Shuffling: Ignore shorts option dropdown -----
domElements.shuffleIgnoreShortsOptionDropdown.value = configSync.shuffleIgnoreShortsOption;

// ----- Shuffling: Open as playlist option toggle -----
domElements.shuffleOpenAsPlaylistOptionToggle.checked = configSync.shuffleOpenAsPlaylistOption;
Expand Down Expand Up @@ -204,11 +204,11 @@ async function setPopupDomElemenEventListeners(domElements) {
manageDependents(domElements, domElements.shuffleReUseNewTabOptionToggle, this.checked);
});

// Shuffling: Ignore shorts option toggle
domElements.shuffleIgnoreShortsOptionToggle.addEventListener("change", async function () {
await setSyncStorageValue("shuffleIgnoreShortsOption", this.checked);
// Shuffling: Ignore shorts option dropdown
domElements.shuffleIgnoreShortsOptionDropdown.addEventListener("change", async function () {
await setSyncStorageValue("shuffleIgnoreShortsOption", this.value);

manageDependents(domElements, domElements.shuffleIgnoreShortsOptionToggle, this.checked);
manageDependents(domElements, domElements.shuffleIgnoreShortsOptionDropdown, this.value);
});

// Shuffling: Open as playlist option toggle
Expand Down
77 changes: 62 additions & 15 deletions src/shuffleVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -697,11 +697,15 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd
let allUnknownType = Object.assign({}, playlistInfo["videos"]["unknownType"], playlistInfo["newVideos"] ?? {});
let allKnownVideos = playlistInfo["videos"]["knownVideos"];
let allKnownShorts = playlistInfo["videos"]["knownShorts"];
let allVideos;

let allVideos = Object.assign({}, allUnknownType, allKnownVideos);
// Only if we are not ignoring shorts, we want to include them in the shuffle
if (!configSync.shuffleIgnoreShortsOption) {
allVideos = Object.assign({}, allVideos, allKnownShorts);
// 0 = only shorts, 1 = no option set (shorts are included), 2 = ignore shorts
if (configSync.shuffleIgnoreShortsOption == "1") {
allVideos = Object.assign({}, allUnknownType, allKnownVideos, allKnownShorts);
} else if (configSync.shuffleIgnoreShortsOption == "2") {
allVideos = Object.assign({}, allUnknownType, allKnownVideos);
} else {
allVideos = Object.assign({}, allUnknownType, allKnownShorts);
}

let videosByDate = Object.keys(allVideos).sort((a, b) => {
Expand All @@ -716,7 +720,7 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd

const numVideosToChoose = configSync.shuffleOpenAsPlaylistOption ? configSync.shuffleNumVideosInPlaylist : 1;

console.log(`Choosing ${numVideosToChoose} random video${numVideosToChoose > 1 ? "s" : ""}.`);
console.log(`Choosing ${numVideosToChoose} random video(s).`);

// 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:
Expand Down Expand Up @@ -750,7 +754,7 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd
throw new RandomYoutubeVideoError(
{
code: "RYV-6B",
message: "All previously uploaded videos on this channel were deleted (the channel does not have any uploads) or you are ignoring shorts and the channel has only uploaded shorts.",
message: "All previously uploaded videos on this channel were deleted (the channel does not have any uploads) or you are ignoring/only shuffling from shorts and the channel only has/has no shorts.",
solveHint: "If you are ignoring shorts, disable the option in the popup to shuffle from this channel.",
showTrace: false
}
Expand All @@ -761,17 +765,31 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd
console.log(`No more videos to choose from (${numVideosToChoose - i} videos too few uploaded on channel).`);
break outerLoop;
}
/* c8 ignore stop */
}
/* c8 ignore stop */
} while (!await testVideoExistence(randomVideo))
}

// If the user does not want to shuffle from shorts, and we do not yet know the type of the chosen video, we check if it is a short
if (configSync.shuffleIgnoreShortsOption) {
// 0 = only shorts, 1 = no option set (shorts are included), 2 = ignore shorts
// If the user does not want to shuffle from shorts, or only wants to shuffle from shorts, and we do not yet know the type of the chosen video, we check if it is a short or not
if (configSync.shuffleIgnoreShortsOption != "1") {
if (playlistInfo["videos"]["unknownType"][randomVideo] !== undefined) {
const videoIsShort = await isShort(randomVideo);

if (videoIsShort) {
// 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
playlistInfo["videos"]["knownVideos"][randomVideo] = playlistInfo["videos"]["unknownType"][randomVideo];
delete playlistInfo["videos"]["unknownType"][randomVideo];

// The video is not a short, so add it to the list of chosen videos and remove it from the pool of videos to choose from
chosenVideos.push(randomVideo);
videosToShuffle.splice(videosToShuffle.indexOf(randomVideo), 1);

// Case 2: isShort && ignoreShorts => Failure
} else if (videoIsShort && configSync.shuffleIgnoreShortsOption == "2") {
console.log('A chosen video was a short, but shorts are ignored. Choosing a new random video.');

// Move the video to the knownShorts subdictionary
Expand All @@ -784,15 +802,44 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd

// We need to decrement i, as we did not choose a video in this iteration
i--;
} else {

// Case 3: isShort && onlyShorts => Success
} else if (videoIsShort && configSync.shuffleIgnoreShortsOption == "0") {
// Move the video to the knownShorts subdictionary
playlistInfo["videos"]["knownShorts"][randomVideo] = playlistInfo["videos"]["unknownType"][randomVideo];
delete playlistInfo["videos"]["unknownType"][randomVideo];

// The video is a short, so add it to the list of chosen videos and remove it from the pool of videos to choose from
chosenVideos.push(randomVideo);
videosToShuffle.splice(videosToShuffle.indexOf(randomVideo), 1);

// Case 4: !isShort && onlyShorts => Failure
} else if (!videoIsShort && configSync.shuffleIgnoreShortsOption == "0") {
console.log('A chosen video was not a short, but only shorts should be shuffled. Choosing a new random video.');

// Move the video to the knownVideos subdictionary
playlistInfo["videos"]["knownVideos"][randomVideo] = playlistInfo["videos"]["unknownType"][randomVideo];
delete playlistInfo["videos"]["unknownType"][randomVideo];

// The video is not a short, so add it to the list of chosen videos and remove it from the pool of videos to choose from
chosenVideos.push(randomVideo);
// Remove the video from videosToShuffle to not choose it again
// Do not remove it from the playlistInfo object, as we do not want to delete it from the database
videosToShuffle.splice(videosToShuffle.indexOf(randomVideo), 1);

// We need to decrement i, as we did not choose a video in this iteration
i--;

/* c8 ignore start - This should never happen, but we want to test it anyway */
} else {
throw new RandomYoutubeVideoError(
{
code: "RYV-11B",
message: `An unknown error occurred while testing if the video ${randomVideo} should be included in the shuffle.`,
solveHint: "Please contact the developer, as this should not happen.",
showTrace: false
}
);
}
/* c8 ignore stop */
} else {
// Otherwise, the video must be a knownVideo, as we do not include knownShorts in allVideos
chosenVideos.push(randomVideo);
Expand All @@ -810,7 +857,7 @@ async function chooseRandomVideosFromPlaylist(playlistInfo, channelId, shouldUpd
throw new RandomYoutubeVideoError(
{
code: "RYV-6B",
message: "All previously uploaded videos on this channel were deleted (the channel does not have any uploads) or you are ignoring shorts and the channel has only uploaded shorts.",
message: "All previously uploaded videos on this channel were deleted (the channel does not have any uploads) or you are ignoring/only shuffling from shorts and the channel only has/has no shorts.",
solveHint: "If you are ignoring shorts, disable the option in the popup to shuffle from this channel.",
showTrace: false
}
Expand All @@ -832,7 +879,7 @@ function getVideoType(videoId, playlistInfo) {
/* c8 ignore start - This will only happen if we forget to implement something here, so we do not need to test it */
throw new RandomYoutubeVideoError(
{
code: "RYV-11",
code: "RYV-11A",
message: `The video that was tested does not exist in the local playlist ${videoId}, so it's type could not be found.`,
solveHint: "Please contact the developer, as this should not happen.",
showTrace: false
Expand Down
Loading

0 comments on commit 3f7537a

Please sign in to comment.