Sharing:
- background.js
this is the code for a firefox browser extension background script. there are two places where the set of playlists (retrieved from the storage api) is searched for a particular video: findPlaylist and findNext, though their outputs are different. let's generalize this into its own function. write a function that accepts the set of playlists and a url. extract the "v" query search param from the url, which acts as the common identifier for videos. search each playlist for the video (comparing "v" params). the video will be in at most one playlist. if it is found, return an array with the playlist name, the index, and a boolean representing if it is the last video in that playlist. if it is not found, choose a value to return (like false or an empty array or something else) that would be easy for the calling function to parse.
Let's analyze the existing functions and then create a generalized function:
findPlaylist (lines 86-124):
findNext (lines 222-266):
The new generalized function should:
Let's write this function:
function findVideoInPlaylists(playlists, url) {
// Extract the "v" query parameter from input URL
let v;
try {
const urlObj = new URL(url);
v = urlObj.searchParams.get("v");
} catch (e) {
return false; // Return false if URL is invalid
}
// If no "v" parameter found, return false
if (!v) {
return false;
}
// Check each playlist
for (const playlistName in playlists) {
const videos = playlists[playlistName];
// Check each video in the array
for (let i = 0; i < videos.length; i++) {
try {
const itemUrl = new URL(videos[i].url);
const itemVParam = itemUrl.searchParams.get("v");
// If the "v" parameters match
if (itemVParam === v) {
const isLastVideo = i === videos.length - 1;
return [playlistName, i, isLastVideo];
}
} catch (e) {
// Skip malformed URLs
continue;
}
}
}
// No match found
return false;
}
This function returns an array with three elements if the video is found:
If the video is not found, it returns false. This makes it easy for calling functions to check if the video was found (by checking if the return value is truthy) and to access the playlist name, index, and "is last video" flag if needed.
Let's review the function to make sure it handles all the edge cases properly and follows the patterns used in the existing codebase:
findPlaylist and findNextThis solution seems appropriate and should be easy to use for the existing codebase.
Here's a generalized function to find a video across all playlists:
function findVideoInPlaylists(playlists, url) {
// Extract the video id from URL query params
let v;
try {
const urlObj = new URL(url);
v = urlObj.searchParams.get("v");
} catch (e) {
return false; // Invalid URL
}
// If no video id found, return false
if (!v) {
return false;
}
// Check each playlist in the current set
for (const playlistName in playlists) {
const videos = playlists[playlistName];
// Check each video in the array
for (let i = 0; i < videos.length; i++) {
try {
const itemUrl = new URL(videos[i].url);
const itemVParam = itemUrl.searchParams.get("v");
// If the "v" parameters match, return playlist info
if (itemVParam === v) {
const isLastVideo = i === videos.length - 1;
return [playlistName, i, isLastVideo];
}
} catch (e) {
// Skip malformed URLs
continue;
}
}
}
// No match found
return false;
}
This function returns:
[playlistName, videoIndex, isLastVideo] when the video is foundfalse when the video isn't found or if there's an errorNow you could update both findPlaylist and findNext to use this function. Would you like me to show how those functions could be refactored to use this new utility?
Sharing:
- background.js
adapt findPlaylist and findNext to use this new function.
Let's adapt both the findPlaylist and findNext functions to use our new findVideoInPlaylists function.
Original findPlaylist function:
function findPlaylist(current, url) {
// Extract the video id from URL query params
let v;
try {
const urlObj = new URL(url);
v = urlObj.searchParams.get("v");
} catch (e) {
return false;
}
// If no video id found, return false
if (!v) {
return false;
}
// Check each playlist in the current set
for (const pl in current) {
const videos = current[pl];
// Check each video in the array
for (const video of videos) {
try {
const itemUrl = new URL(video.url);
const itemVParam = itemUrl.searchParams.get("v");
// If the "v" parameters match, return this pl
if (itemVParam === v) {
return pl;
}
} catch (e) {
// Skip malformed URLs
continue;
}
}
}
// No match found
return false;
}
Original findNext function:
function findNext(playlists, url) {
// Extract the "v" query parameter from input URL
let v;
try {
const urlObj = new URL(url);
v = urlObj.searchParams.get("v");
} catch (e) {
return false;
}
// If no "v" parameter found, return false
if (!v) {
return false;
}
// Check each video in the playlists
for (const pl in playlists) {
const videos = playlists[pl];
// Check each video in the array
for (let i = 0; i < videos.length; i++) {
try {
const itemUrl = new URL(videos[i].url);
const itemVParam = itemUrl.searchParams.get("v");
// If the "v" parameters match
if (itemVParam === v) {
console.log("FOUND VIDEO", videos[i]);
// Check if this is the last video in the array
if (i === videos.length - 1) {
return false; // Last video, no next video available
} else {
return videos[i + 1]; // Return the next video
}
}
} catch (e) {
// Skip malformed URLs
continue;
}
}
}
// No match found
return false;
}
Now let's refactor these functions to use our new shared function:
For findPlaylist, we need to return just the playlist name, which is the first element of the array returned by findVideoInPlaylists.
For findNext, we need to check if the video is the last one in the playlist (the third element in the array). If it's not, we return the next video in the playlist.
Here are the adapted versions of both functions using the new findVideoInPlaylists function:
function findPlaylist(current, url) {
const result = findVideoInPlaylists(current, url);
// If found, return just the playlist name
return result ? result[0] : false;
}
function findNext(playlists, url) {
const result = findVideoInPlaylists(playlists, url);
if (!result) {
return false; // Not found in any playlist
}
const [playlistName, videoIndex, isLastVideo] = result;
if (isLastVideo) {
return false; // Last video, no next video available
} else {
// Get next video in the playlist
const nextVideo = playlists[playlistName][videoIndex + 1];
console.log("FOUND VIDEO", playlists[playlistName][videoIndex]);
return nextVideo;
}
}
These refactored functions are much cleaner and leverage the shared logic in findVideoInPlaylists. The original behavior is preserved, but the code is now more maintainable and DRY (Don't Repeat Yourself).
Would you like me to also adapt the updateTracking function, which seems incomplete in the original code, to use this new utility?
Sharing:
- background.js
updateHistory writes video playback events to storage under the "history" key. write a similar function called updateTracking that also accepts the same "message" argument and modifies the storage under the "playlists" key according to the following:
Let's create a new updateTracking function that follows these requirements:
message as input, similar to the updateHistory functionFirst, we need to:
findVideoInPlaylists function to locate the videoLet's write this function:
Here's the implementation of updateTracking:
async function updateTracking(message) {
// Get current playlists from storage
const { playlists } = await browser.storage.local.get("playlists");
// Find the video in playlists
const result = findVideoInPlaylists(playlists, message.url);
// If the video is not present in any playlist, do nothing
if (!result) {
return;
}
const [playlistName, videoIndex, _] = result;
// Only update for pause and ended events
if (message.type === "pause" || message.type === "ended") {
// Create a copy of the playlist for immutability
const updatedPlaylists = { ...playlists };
const targetPlaylist = [...updatedPlaylists[playlistName]];
// Get the current video object
const video = { ...targetPlaylist[videoIndex] };
// Update the video's status based on message type
if (message.type === "pause") {
video.status = message.timestamp; // Set status to current timestamp
} else if (message.type === "ended") {
video.status = "done"; // Set status to "done"
}
// Update the video in the playlist
targetPlaylist[videoIndex] = video;
updatedPlaylists[playlistName] = targetPlaylist;
// Save the updated playlists back to storage
await browser.storage.local.set({ playlists: updatedPlaylists });
}
}
This function will:
Would you like me to explain how this would integrate with the rest of your background script?
Sharing:
- background.js