diff --git a/background/index.html b/background/index.html
deleted file mode 100644
index f724de8..0000000
--- a/background/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- RadioWave player
-
-
-
-
-
-
-
diff --git a/background/offscreen.html b/background/offscreen.html
new file mode 100644
index 0000000..fcd4c58
--- /dev/null
+++ b/background/offscreen.html
@@ -0,0 +1,10 @@
+
+
+
+
+ RadioWave Audio Player
+
+
+
+
+
diff --git a/background/offscreen.js b/background/offscreen.js
new file mode 100644
index 0000000..f198e36
--- /dev/null
+++ b/background/offscreen.js
@@ -0,0 +1,22 @@
+"use strict";
+
+const audio = new Audio();
+
+chrome.runtime.onMessage.addListener((message) => {
+ switch (message.type) {
+ case 'AUDIO_PLAY':
+ audio.volume = message.volume || 0.3;
+ audio.src = message.url;
+ audio.play().catch(err => console.error('Audio play error:', err));
+ break;
+
+ case 'AUDIO_STOP':
+ audio.pause();
+ audio.src = '';
+ break;
+
+ case 'AUDIO_VOLUME':
+ audio.volume = message.volume;
+ break;
+ }
+});
diff --git a/background/player.js b/background/player.js
deleted file mode 100644
index a049e21..0000000
--- a/background/player.js
+++ /dev/null
@@ -1,28 +0,0 @@
-"use strict";
-
-const STREAM_API_URL = "https://europe-southwest1-radio--wave.cloudfunctions.net/getstream-v2";
-const VERSION = `version=${localStorage.version}`;
-const CLIENT = "client=chrome-extension";
-
-window.backgroundPlayer = new class BackgroundPlayer {
- audio = new Audio();
-
- play() {
- this.volume();
- this.audio.src = `${STREAM_API_URL}?station=${localStorage.station}&${CLIENT}&${VERSION}`;
- this.audio.play();
-
- localStorage.setItem("state", "played");
- }
-
- stop() {
- this.audio.pause();
- this.audio.src = "";
-
- localStorage.setItem("state", "paused");
- }
-
- volume() {
- this.audio.volume = localStorage.volume / 100;
- }
-}();
diff --git a/background/service-worker.js b/background/service-worker.js
new file mode 100644
index 0000000..15efdde
--- /dev/null
+++ b/background/service-worker.js
@@ -0,0 +1,89 @@
+"use strict";
+
+// Import stations list
+importScripts('../shared/stations.js');
+
+const STREAM_API_URL = "https://europe-southwest1-radio--wave.cloudfunctions.net/getstream-v2";
+const CLIENT = "client=chrome-extension";
+const currentVersion = "3.0.0";
+
+// Initialize storage on install
+chrome.runtime.onInstalled.addListener(async () => {
+ const data = await chrome.storage.local.get(['version', 'volume', 'station', 'state']);
+
+ if (!data.version || data.version !== currentVersion) {
+ const isCurrentStationExist = self.stationList && self.stationList.some(
+ item => data.station === `${item.group}.${item.station}`
+ );
+
+ await chrome.storage.local.set({
+ version: currentVersion,
+ volume: data.volume || 30,
+ state: "paused",
+ station: isCurrentStationExist ? data.station : "TVR.KissFM"
+ });
+ }
+});
+
+// Create offscreen document for audio playback
+async function setupOffscreenDocument() {
+ const existingContexts = await chrome.runtime.getContexts({
+ contextTypes: ['OFFSCREEN_DOCUMENT'],
+ });
+
+ if (existingContexts.length > 0) {
+ return;
+ }
+
+ await chrome.offscreen.createDocument({
+ url: 'background/offscreen.html',
+ reasons: ['AUDIO_PLAYBACK'],
+ justification: 'Play radio audio stream',
+ });
+}
+
+// Handle messages from popup
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ (async () => {
+ switch (message.type) {
+ case 'PLAY':
+ await setupOffscreenDocument();
+ const playData = await chrome.storage.local.get(['station', 'volume', 'version']);
+ const playUrl = `${STREAM_API_URL}?station=${playData.station}&${CLIENT}&version=${playData.version}`;
+
+ chrome.runtime.sendMessage({
+ type: 'AUDIO_PLAY',
+ url: playUrl,
+ volume: playData.volume / 100
+ });
+
+ await chrome.storage.local.set({ state: 'played' });
+ sendResponse({ success: true });
+ break;
+
+ case 'STOP':
+ chrome.runtime.sendMessage({ type: 'AUDIO_STOP' });
+ await chrome.storage.local.set({ state: 'paused' });
+ sendResponse({ success: true });
+ break;
+
+ case 'SET_VOLUME':
+ chrome.runtime.sendMessage({ type: 'AUDIO_VOLUME', volume: message.volume / 100 });
+ await chrome.storage.local.set({ volume: message.volume });
+ sendResponse({ success: true });
+ break;
+
+ case 'SET_STATION':
+ await chrome.storage.local.set({ station: message.station });
+ sendResponse({ success: true });
+ break;
+
+ case 'GET_STATE':
+ const state = await chrome.storage.local.get(['state', 'volume', 'station']);
+ sendResponse(state);
+ break;
+ }
+ })();
+
+ return true; // Keep message channel open for async response
+});
diff --git a/background/updater.js b/background/updater.js
deleted file mode 100644
index 4caf804..0000000
--- a/background/updater.js
+++ /dev/null
@@ -1,22 +0,0 @@
-"use strict";
-
-const currentVersion = "2.2.9";
-
-(() => {
- const { volume, station, version } = localStorage;
-
- if (!version || version !== currentVersion) {
- const isCurrentStationExist = window.stationList.some(item => station === `${item.group}.${item.station}`);
-
- const state = {
- version: currentVersion,
- volume: volume || 30,
- state: "paused",
- station: isCurrentStationExist ? station : "TVR.KissFM"
- };
-
- localStorage.clear();
-
- Object.keys(state).forEach(key => localStorage.setItem(key, state[key]));
- }
-})();
diff --git a/manifest.json b/manifest.json
index 3962578..2a165a8 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,21 +1,22 @@
{
- "background": {
- "page": "background/index.html"
- },
- "browser_action": {
- "default_popup": "popup/index.html"
- },
+ "manifest_version": 3,
+ "name": "Radio Wave",
+ "version": "3.0.0",
"description": "A simple application for listening to online radio.",
"icons": {
- "128": "icons/128.png",
"16": "icons/16.png",
"24": "icons/24.png",
- "256": "icons/256.png",
"32": "icons/32.png",
- "64": "icons/64.png"
+ "64": "icons/64.png",
+ "128": "icons/128.png",
+ "256": "icons/256.png"
},
- "permissions": ["https://europe-southwest1-radio--wave.cloudfunctions.net/*"],
- "manifest_version": 2,
- "name": "Radio Wave",
- "version": "2.2.9"
+ "action": {
+ "default_popup": "popup/index.html"
+ },
+ "background": {
+ "service_worker": "background/service-worker.js"
+ },
+ "permissions": ["storage", "offscreen"],
+ "host_permissions": ["https://europe-southwest1-radio--wave.cloudfunctions.net/*"]
}
diff --git a/popup/application.js b/popup/application.js
index 8177b38..e438c46 100644
--- a/popup/application.js
+++ b/popup/application.js
@@ -2,44 +2,78 @@
const getStationId = (group, station) => `${group}.${station}`;
-const backgroundPlayer = chrome.extension.getBackgroundPage().backgroundPlayer;
-
const controlPlay = document.getElementById("cnt_play");
const controlVolume = document.getElementById("cnt_volume");
const playList = document.getElementById("play_list");
+let currentState = { state: "paused", volume: 30, station: "TVR.KissFM" };
+
+// Initialize popup with current state
+async function initializePopup() {
+ currentState = await chrome.runtime.sendMessage({ type: 'GET_STATE' });
+
+ controlPlay.setAttribute("class", currentState.state);
+ controlVolume.value = currentState.volume;
+
+ playList.innerHTML = window.stationList
+ .map(({ name, group, station }) => {
+ const stationId = getStationId(group, station);
+
+ return `
+ ${group}
+ ${name}
+ `;
+ })
+ .join("");
+
+ if (document.querySelector(".selected")) {
+ document.querySelector(".selected").scrollIntoView();
+ }
+}
+
// Play/Pause control
-controlPlay.addEventListener("click", () => {
- if (localStorage.state === "paused") {
- backgroundPlayer.play();
+controlPlay.addEventListener("click", async () => {
+ if (currentState.state === "paused") {
+ await chrome.runtime.sendMessage({ type: 'PLAY' });
+ currentState.state = "played";
} else {
- backgroundPlayer.stop();
+ await chrome.runtime.sendMessage({ type: 'STOP' });
+ currentState.state = "paused";
}
- controlPlay.setAttribute("class", localStorage.state);
+ controlPlay.setAttribute("class", currentState.state);
});
// Volume control
-controlVolume.addEventListener("input", event => {
- localStorage.volume = event.target.value;
-
- backgroundPlayer.volume();
+controlVolume.addEventListener("input", async (event) => {
+ const volume = event.target.value;
+ currentState.volume = volume;
+
+ await chrome.runtime.sendMessage({
+ type: 'SET_VOLUME',
+ volume: parseInt(volume)
+ });
});
-controlVolume.addEventListener("mousewheel", e => {
- const value = +localStorage.volume + e.wheelDelta / 24;
+controlVolume.addEventListener("mousewheel", async (e) => {
+ const value = +currentState.volume + e.wheelDelta / 24;
const volume = value < 0 ? 0 : value > 100 ? 100 : value;
controlVolume.value = volume;
- localStorage.volume = volume;
+ currentState.volume = volume;
- backgroundPlayer.volume();
+ await chrome.runtime.sendMessage({
+ type: 'SET_VOLUME',
+ volume: parseInt(volume)
+ });
});
// List control
-playList.addEventListener("click", event => {
+playList.addEventListener("click", async (event) => {
const element = event.target.closest("li");
+ if (!element) return;
+
if (document.querySelector(".selected")) {
document.querySelector(".selected").setAttribute("class", "");
}
@@ -47,28 +81,17 @@ playList.addEventListener("click", event => {
element.setAttribute("class", "selected");
controlPlay.setAttribute("class", "played");
- localStorage.station = element.getAttribute("data-id");
+ const stationId = element.getAttribute("data-id");
+ currentState.station = stationId;
+ currentState.state = "played";
- backgroundPlayer.play();
-});
+ await chrome.runtime.sendMessage({
+ type: 'SET_STATION',
+ station: stationId
+ });
-// Render station list
-(() => {
- controlPlay.setAttribute("class", localStorage.state);
- controlVolume.value = localStorage.volume;
-
- playList.innerHTML = window.stationList
- .map(({ name, group, station }) => {
- const stationId = getStationId(group, station);
-
- return `
- ${group}
- ${name}
- `;
- })
- .join("");
+ await chrome.runtime.sendMessage({ type: 'PLAY' });
+});
- if (document.querySelector(".selected")) {
- document.querySelector(".selected").scrollIntoView();
- }
-})();
+// Initialize on load
+initializePopup();
diff --git a/shared/stations.js b/shared/stations.js
index 08c54a7..9e5f2ba 100644
--- a/shared/stations.js
+++ b/shared/stations.js
@@ -1,4 +1,4 @@
-window.stationList = [
+const stationList = [
{
name: "MFM",
group: "UA",
@@ -59,3 +59,10 @@ window.stationList = [
station: "RadioBayraktar"
}
];
+
+// Support both window context (popup/offscreen) and service worker (self)
+if (typeof window !== 'undefined') {
+ window.stationList = stationList;
+} else if (typeof self !== 'undefined') {
+ self.stationList = stationList;
+}