+
+
+
} usernames - List of usernames to check.
+ * @param {Function} callback - Callback function to execute after fetching status.
+ * @param {boolean} sendNotification - Whether to send notifications for online streamers.
+ */
+const fetchStreamerStatus = async (usernames, callback, sendNotification = true) => {
+ try {
+ const response = await fetch(CHANNEL_API_URI, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ body: JSON.stringify({channels: usernames.map((username) => username.toLowerCase())}),
+ });
+
+ const streamersLive = await response.json();
+ const {knownOnlineStreamers = {}} = await chrome.storage.local.get('knownOnlineStreamers');
+
+ updateKnownStreamers(streamersLive, knownOnlineStreamers);
+
+ const numOnline = Object.keys(streamersLive).length;
+ updateBadgeAndTitle(numOnline, streamersLive);
+
+ if (sendNotification) {
+ await notifyNewStreamers(streamersLive, knownOnlineStreamers);
+ }
- chrome.storage.sync.get('hideStreamersOnlineCount', (storage) => {
- if (!storage.hideStreamersOnlineCount) {
- chrome.action.setBadgeText({
- text: `${numOnline > 0 ? numOnline : ''}`,
- });
- }
- });
-
- chrome.action.setTitle({
- title: `${numOnline} channels online. ${Object.keys(streamersLive).join(
- ', '
- )}`,
- });
-
- const currentStreamers = Object.keys(streamersLive);
- for (let i = 0; i < currentStreamers.length; i++) {
- const streamer = currentStreamers[i];
- const streamData = streamersLive[streamer];
- const alreadySentNotification = knownOnlineStreamers[streamer];
- if (!alreadySentNotification) {
- knownOnlineStreamers[streamer] = true;
- if (sendNotification) {
- await createNotification(streamData);
- }
+ await chrome.storage.local.set({knownOnlineStreamers});
+ const hydratedData = hydrateStreamData(usernames, streamersLive);
+ callback(hydratedData);
+ } catch (error) {
+ console.error('Error fetching streamer status:', error);
}
- }
+};
- await chrome.storage.local.set({ knownOnlineStreamers });
+/**
+ * Updates the known streamers by removing offline ones.
+ * @param {Object} streamersLive - Object containing live streamers.
+ * @param {Object} knownOnlineStreamers - Object containing known online streamers.
+ */
+const updateKnownStreamers = (streamersLive, knownOnlineStreamers) => {
+ Object.keys(knownOnlineStreamers).forEach((streamer) => {
+ if (!streamersLive[streamer]) {
+ delete knownOnlineStreamers[streamer];
+ }
+ });
+};
- const hydratedData = usernames.map((username) => {
- return streamersLive[username] || { username };
- });
+/**
+ * Updates the extension's badge text and title.
+ * @param {number} numOnline - Number of online streamers.
+ * @param {Object} streamersLive - Object containing live streamers.
+ */
+const updateBadgeAndTitle = (numOnline, streamersLive) => {
+ chrome.storage.sync.get('hideStreamersOnlineCount', (storage) => {
+ if (!storage.hideStreamersOnlineCount) {
+ chrome.action.setBadgeText({text: `${numOnline > 0 ? numOnline : ''}`});
+ }
+ });
- callback(hydratedData);
+ chrome.action.setTitle({
+ title: `${numOnline} channels online. ${Object.keys(streamersLive).join(', ')}`,
+ });
};
-const handleClick = (id) => {
- chrome.storage.local.get(['notifications']).then((result) => {
- const url = 'https://twitch.tv/' + result.notifications[id];
- chrome.tabs.create({ url: url });
- });
+/**
+ * Sends notifications for new online streamers.
+ * @param {Object} streamersLive - Object containing live streamers.
+ * @param {Object} knownOnlineStreamers - Object containing known online streamers.
+ */
+const notifyNewStreamers = async (streamersLive, knownOnlineStreamers) => {
+ for (const streamer of Object.keys(streamersLive)) {
+ if (!knownOnlineStreamers[streamer]) {
+ knownOnlineStreamers[streamer] = true;
+ await createNotification(streamersLive[streamer]);
+ }
+ }
};
-const onRequest = (request, sender, callback) => {
- if (request.action == 'fetchStreamerStatus') {
- fetchStreamerStatus(request.usernames, callback);
- } else if (request.action === 'setBadgeText') {
- const setBadgeText = request.setBadgeText;
+/**
+ * Hydrates stream data with usernames that may not be live.
+ * @param {Array} usernames - List of usernames to check.
+ * @param {Object} streamersLive - Object containing live streamers.
+ * @returns {Array
-
-
-
-
-
-
Streamers
+
+
+
+
+
+ Streamers
+
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
- -
-- Add a streamer below to start tracking their status. -
-
+
-
+
+ +
++ Add a streamer below to start tracking their status. +
+
+
+
-
+
+
diff --git a/extension/scripts/background.js b/extension/scripts/background.js
index b72bbca..0e59327 100644
--- a/extension/scripts/background.js
+++ b/extension/scripts/background.js
@@ -1,154 +1,215 @@
const CHANNEL_API_URI = 'https://twitch.theorycraft.gg/channel-status';
-
+const PREVIEW_IMAGE_BASE_URL = 'https://static-cdn.jtvnw.net/previews-ttv/live_user_';
+const NOTIFICATION_ICON_URL = '../images/logo_128.png';
+const BADGE_COLOR = '#5cb85c';
+const BACKGROUND_FETCH_ALARM_NAME = 'backgroundFetch';
+const FETCH_INTERVAL_MINUTES = 5;
+
+/**
+ * Generates a URL for the stream preview image.
+ * @param {string} userName - The username of the streamer.
+ * @param {number} width - Width of the preview image.
+ * @param {number} height - Height of the preview image.
+ * @returns {string} - URL of the preview image.
+ */
const getPreviewUrl = (userName, width, height) =>
- `https://static-cdn.jtvnw.net/previews-ttv/live_user_${userName}-${width}x${height}.jpg`;
+ `${PREVIEW_IMAGE_BASE_URL}${userName}-${width}x${height}.jpg`;
+/**
+ * Creates and displays a Chrome notification.
+ * @param {Object} stream - Stream data for the notification.
+ */
const createNotification = async (stream) => {
- const imageResponse = await fetch(getPreviewUrl(stream.username, 640, 360));
- const imageData = await imageResponse.blob();
-
- const reader = new FileReader();
- reader.readAsDataURL(imageData);
- reader.onloadend = () => {
- const base64data = reader.result;
- const opt = {
- type: 'image',
- title: stream.channel.display_name + ' playing ' + stream.game,
- message: stream.channel.status,
- iconUrl: '../images/logo_128.png',
- imageUrl: base64data,
- buttons: [
- {
- title: 'View Stream',
- },
- ],
- };
-
- chrome.notifications.create(Math.random().toString(36), opt, (id) => {
- chrome.storage.session.get(['notifications']).then((result) => {
- if (!result.notifications) {
- result.notifications = {};
- }
- result.notifications[id] = stream.username;
- chrome.storage.session.set({ notifications: result.notifications });
- });
- });
- };
-};
-
-const fetchStreamerStatus = async (
- usernames,
- callback,
- sendNotification = true
-) => {
- const response = await fetch(CHANNEL_API_URI, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- },
- body: JSON.stringify({
- channels: usernames.map((username) => username.toLowerCase()),
- }),
- });
-
- const streamersLive = await response.json();
-
- const knownOnlineStreamersData = await chrome.storage.local.get([
- 'knownOnlineStreamers',
- ]);
-
- const knownOnlineStreamers =
- knownOnlineStreamersData && knownOnlineStreamersData.knownOnlineStreamers
- ? knownOnlineStreamersData.knownOnlineStreamers
- : {};
-
- for (let streamer of Object.keys(knownOnlineStreamers)) {
- if (!streamersLive[streamer]) {
- delete knownOnlineStreamers[streamer];
+ try {
+ const imageResponse = await fetch(getPreviewUrl(stream.username, 640, 360));
+ const imageData = await imageResponse.blob();
+
+ const reader = new FileReader();
+ reader.readAsDataURL(imageData);
+ reader.onloadend = () => {
+ const base64data = reader.result;
+ const opt = {
+ type: 'image',
+ title: `${stream.channel.display_name} playing ${stream.game}`,
+ message: stream.channel.status,
+ iconUrl: NOTIFICATION_ICON_URL,
+ imageUrl: base64data,
+ buttons: [{title: 'View Stream'}],
+ };
+
+ chrome.notifications.create(Math.random().toString(36), opt, (id) => {
+ chrome.storage.session.get(['notifications']).then((result) => {
+ const notifications = result.notifications || {};
+ notifications[id] = stream.username;
+ chrome.storage.session.set({notifications});
+ });
+ });
+ };
+ } catch (error) {
+ console.error('Error creating notification:', error);
}
- }
+};
- const numOnline = Object.keys(streamersLive).length;
+/**
+ * Fetches the online status of streamers and optionally sends notifications.
+ * @param {Array
-
-
+
-