Модернизация расширения часть 2

Гобелен - кликните для возврата на главную

Не прошло и месяца а Фурмановская ЦРБ в очередной раз попала в историю. На этот раз сотрудница клеветала на пациентов, а именно огласку получил случай когда сотрудница назвала пациента алкашём.

Так почему бы не начать использовать обход блокировки YouTube при том что техническая возможность используя расширение nikolaevevge для браузера Mozilla Firefox уже появилась, а объем полезной(и не очень) информации на видеохостинге YouTube, по мнению некоторых диванных экспертов, меньше не стал.

Ровно 3 года назад произошло событие, которое подарило жителям планеты Змеля новый чистый праздник 6 апреля - в этот замечательный день земля забрала гнить негодяя и СПАМера Владимира Вольфовича Жириновского.

Как бы не обстояли дела в области культуры и IT-технологий, Самосвал писал статьи на связанные темы и планирует ещё написать.

На очередном конкурсе танцев, школьница отлично выступила.

Что использовать вместо Юбуст

Вот та самая информация: программа для продвижения канала YouTube, эта информация выложена уже несколько лет, при этом просмотров не много, а метод который описан в данном видео действительно помогает продвигать видео в YouTube.

Начал разбираться с информацией которая находится в HTTPS клиентском запросе рукопожатия.

Обратите внимание! Объект изображённый на гобилене может отличаться от общепринятого вида описанного объекта. Тут дело в том что художник видит именно так!

104 549 руб.

Описание товара

Давайте не будем использовать прокси по умолчанию, вместо этого добавьте в окне расширения внизу(под всеми переключателями) статус(может быть равен: 1)Инициализация(жёлтый - пока идёт загрузка дынных о IP прокси серверов); 2) Готов к работе(зелёный - когда настройки прокси загружены с удалённого сервера); 3) Ошибка(красный - если не удалось загрузить настройки прокси)). Верните код исправленных файлов целиком, а не только местоисправления.

Вот полные коды всех изменённых файлов с добавлением статуса работы расширения:

Файл background.js

javascript const DEFAULT_SETTINGS = { masterSwitch: true, googlevideo: true, youtube: true, ytimg: true, ggpht: true }; let currentSettings = {...DEFAULT_SETTINGS}; let proxyListener = null; let PROXY = null; let PROXY2 = null; let proxyStatus = "initializing"; // "initializing", "ready", "error" async function fetchProxies() { try { proxyStatus = "initializing"; updateBrowserAction(); const response = await fetch('https://10-11/getProxDataBeta.php'); const data = await response.json(); PROXY = { host: data.en.ip, port: data.en.port }; PROXY2 = { host: data.ru.ip, port: data.ru.port }; proxyStatus = "ready"; console.log('Прокси успешно получены:', { PROXY, PROXY2 }); } catch (error) { proxyStatus = "error"; console.error('Ошибка при получении прокси:', error); } finally { updateBrowserAction(); } } function updateBrowserAction() { let iconPath, title; switch (proxyStatus) { case "initializing": iconPath = "icon_yellow.png"; title = "Proxy Control - Инициализация"; break; case "ready": iconPath = "icon.png"; title = "Proxy Control - Готов к работе"; break; case "error": iconPath = "icon_red.png"; title = "Proxy Control - Ошибка"; break; default: iconPath = "icon.png"; title = "Proxy Control"; } browser.browserAction.setIcon({ path: iconPath }); browser.browserAction.setTitle({ title }); } async function getSettings() { const settings = await browser.storage.local.get(DEFAULT_SETTINGS); currentSettings = {...DEFAULT_SETTINGS, ...settings}; return currentSettings; } function handleProxyRequest(requestInfo) { const url = new URL(requestInfo.url); const hostname = url.hostname; if (!currentSettings.masterSwitch || proxyStatus !== "ready" || !PROXY || !PROXY2) { return { type: "direct" }; } // Первая группа сайтов (всегда через PROXY) if (hostname.endsWith('instagram.com') || hostname.endsWith('fbcdn.net') || hostname.endsWith('chatgpt.com') || hostname.endsWith('fb.com') || hostname.endsWith('facebook.com') || hostname.endsWith('ionos.com')) { console.log(`Проксируем (PROXY1) запрос к: ${hostname}`); return { type: "socks", host: PROXY.host, port: PROXY.port, proxyDNS: true, failoverTimeout: 5 }; } // Вторая группа сайтов (зависит от настроек) if (hostname.endsWith('googlevideo.com') && currentSettings.googlevideo) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('youtube.com') && currentSettings.youtube) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('ytimg.com') && currentSettings.ytimg) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('ggpht.com') && currentSettings.ggpht) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } return { type: "direct" }; } async function setupProxy() { if (proxyListener) { browser.proxy.onRequest.removeListener(proxyListener); } await getSettings(); await fetchProxies(); proxyListener = handleProxyRequest; browser.proxy.onRequest.addListener( proxyListener, { urls: ["*://*.googlevideo.com/*", "*://googlevideo.com/*", "*://*.instagram.com/*", "*://instagram.com/*", "*://*.fbcdn.net/*", "*://fbcdn.net/*", "*://*.facebook.com/*", "*://facebook.com/*", "*://*.fb.com/*", "*://fb.com/*", "*://*.youtube.com/*", "*://youtube.com/*", "*://*.chatgpt.com/*", "*://chatgpt.com/*", "*://*.ionos.com/*", "*://ionos.com/*", "*://*.ytimg.com/*", "*://ytimg.com/*", "*://*.ggpht.com/*", "*://ggpht.com/*"] } ); console.log("Прокси обновлено с новыми настройками", currentSettings); } // Инициализация browser.runtime.onStartup.addListener(() => { browser.storage.local.set(DEFAULT_SETTINGS); setupProxy(); }); browser.runtime.onInstalled.addListener(() => { browser.storage.local.set(DEFAULT_SETTINGS); setupProxy(); }); browser.storage.onChanged.addListener(async (changes, area) => { if (area === "local") { console.log("Обнаружены изменения настроек", changes); await setupProxy(); } }); // Обновляем прокси каждые 5 минут setInterval(fetchProxies, 5 * 60 * 1000); setupProxy(); manifest.json: json { "manifest_version": 2, "name": "2ip.ru через SOCKS5", "version": "2.0", "description": "Корректное проксирование только 2ip.ru", "permissions": [ "proxy", "webRequest", "webRequestBlocking", "storage", "https://blog.ivru.net/", "https://10-11/", "*://*.googlevideo.com/*", "*://googlevideo.com/*", "*://*.instagram.com/*", "*://instagram.com/*", "*://*.fbcdn.net/*", "*://fbcdn.net/*", "*://*.facebook.com/*", "*://facebook.com/*", "*://*.fb.com/*", "*://fb.com/*", "*://*.chatgpt.com/*", "*://chatgpt.com/*", "*://*.youtube.com/*", "*://youtube.com/*", "*://*.ionos.com/*", "*://ionos.com/*", "*://*.ytimg.com/*", "*://ytimg.com/*", "*://*.ggpht.com/*", "*://ggpht.com/*" ], "background": { "scripts": ["background.js"], "persistent": true }, "browser_action": { "default_popup": "popup.html", "default_icon": { "48": "icon.png" } }, "content_security_policy": "script-src 'self'; object-src 'self'; connect-src 'self' https://blog.ivru.net/ https://10-11/", "web_accessible_resources": [ "popup.html", "popup.js", "icon.png", "icon_yellow.png", "icon_red.png" ] } popup.html:
HTML
    
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      background-color: #000;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 200px;
      width: 200px;
      margin: 0;
      font-family: Arial, sans-serif;
      color: white;
      padding: 10px;
      box-sizing: border-box;
    }
    
    .container {
      width: 100%;
    }
    
    .header {
      text-align: center;
      margin-bottom: 15px;
      font-weight: bold;
    }
    
    .switch-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }
    
    .switch-label {
      font-size: 14px;
    }
    
    .checkbox-container {
      width: 24px;
      height: 24px;
      border: 2px solid white;
      position: relative;
      box-sizing: border-box;
    }
    
    .switch-track {
      position: absolute;
      width: 4px;
      height: calc(100% - 8px);
      top: 4px;
      background-color: #ff0000;
      transition: all 0.2s ease;
    }
    
    .switch-track {
      left: 4px;
    }
    
    input:checked ~ .checkbox-container .switch-track {
      right: 4px;
      left: auto;
      background-color: #00ff00;
    }
    
    input[type="checkbox"] {
      display: none;
    }
    
    label {
      display: block;
      cursor: pointer;
    }
    
    .divider {
      height: 1px;
      background-color: #333;
      margin: 10px 0;
    }
    
    .status {
      text-align: center;
      margin-top: 10px;
      padding: 5px;
      border-radius: 3px;
      font-size: 12px;
    }
    
    .status-initializing {
      background-color: #FFC107;
      color: black;
    }
    
    .status-ready {
      background-color: #4CAF50;
    }
    
    .status-error {
      background-color: #F44336;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">Proxy Control</div>
    
    <div class="switch-row">
      <span class="switch-label">Master Switch</span>
      <label>
        <input type="checkbox" id="master-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="divider"></div>
    
    <div class="switch-row">
      <span class="switch-label">googlevideo.com</span>
      <label>
        <input type="checkbox" id="googlevideo-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">youtube.com</span>
      <label>
        <input type="checkbox" id="youtube-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ytimg.com</span>
      <label>
        <input type="checkbox" id="ytimg-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ggpht.com</span>
      <label>
        <input type="checkbox" id="ggpht-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div id="status" class="status status-initializing">Инициализация</div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>
popup.js:
      
javascript
async function loadSettings() {
  const result = await browser.storage.local.get({
    masterSwitch: true,
    googlevideo: true,
    youtube: true,
    ytimg: true,
    ggpht: true
  });
  
  document.getElementById('master-switch').checked = result.masterSwitch;
  document.getElementById('googlevideo-switch').checked = result.googlevideo;
  document.getElementById('youtube-switch').checked = result.youtube;
  document.getElementById('ytimg-switch').checked = result.ytimg;
  document.getElementById('ggpht-switch').checked = result.ggpht;
  
  // Получаем статус из фонового скрипта
  const backgroundPage = browser.extension.getBackgroundPage();
  updateStatus(backgroundPage.proxyStatus);
  
  // Слушаем изменения статуса
  browser.runtime.onMessage.addListener((message) => {
    if (message.type === "statusUpdate") {
      updateStatus(message.status);
    }
  });
}
      
function updateStatus(status) {
  const statusElement = document.getElementById('status');
  statusElement.className = 'status';
  
  switch (status) {
    case "initializing":
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Инициализация';
      break;
    case "ready":
      statusElement.classList.add('status-ready');
      statusElement.textContent = 'Готов к работе';
      break;
    case "error":
      statusElement.classList.add('status-error');
      statusElement.textContent = 'Ошибка';
      break;
    default:
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Неизвестный статус';
  }
}
      
async function saveSettings() {
  const settings = {
    masterSwitch: document.getElementById('master-switch').checked,
    googlevideo: document.getElementById('googlevideo-switch').checked,
    youtube: document.getElementById('youtube-switch').checked,
    ytimg: document.getElementById('ytimg-switch').checked,
    ggpht: document.getElementById('ggpht-switch').checked
  };
  
  await browser.storage.local.set(settings);
  console.log("Настройки сохранены", settings);
}
      
document.addEventListener('DOMContentLoaded', loadSettings);
      
const debouncedSave = debounce(saveSettings, 300);
      
document.getElementById('master-switch').addEventListener('change', debouncedSave);
document.getElementById('googlevideo-switch').addEventListener('change', debouncedSave);
document.getElementById('youtube-switch').addEventListener('change', debouncedSave);
document.getElementById('ytimg-switch').addEventListener('change', debouncedSave);
document.getElementById('ggpht-switch').addEventListener('change', debouncedSave);
      
function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

  
Вам также понадобятся три иконки: icon.png (зелёная) - для статуса "Готов к работе" icon_yellow.png (жёлтая) - для статуса "Инициализация" icon_red.png (красная) - для статуса "Ошибка" Основные изменения: Добавлена система статусов (инициализация, готов, ошибка) Добавлено отображение статуса в popup Иконка расширения меняется в зависимости от статуса Удалены прокси по умолчанию Добавлена обработка ошибок при загрузке прокси Добавлено периодическое обновление прокси (каждые 5 минут) Добавлены стили для отображения статуса в popup

Пожалуйста используйте вместо иконок просто текст в попап. Перечитайте пожалуйста ещё раз задание из моего предыдущего сообщения и сделайте в соответствии с ним.

Вот исправленные файлы с текстовым отображением статуса в popup (без изменения иконок):

айл background.js

javaScript
    
const DEFAULT_SETTINGS = {
  masterSwitch: true,
  googlevideo: true,
  youtube: true,
  ytimg: true,
  ggpht: true
};
      
let currentSettings = {...DEFAULT_SETTINGS};
let proxyListener = null;
let PROXY = null;
let PROXY2 = null;
let proxyStatus = "initializing"; // "initializing", "ready", "error"
      
async function fetchProxies() {
  try {
    proxyStatus = "initializing";
    sendStatusUpdate();
    
    const response = await fetch('https://10-11/getProxDataBeta.php');
    const data = await response.json();
    
    PROXY = {
      host: data.en.ip,
      port: data.en.port
    };
    
    PROXY2 = {
      host: data.ru.ip,
      port: data.ru.port
    };
    
    proxyStatus = "ready";
    console.log('Прокси успешно получены:', { PROXY, PROXY2 });
  } catch (error) {
    proxyStatus = "error";
    console.error('Ошибка при получении прокси:', error);
  } finally {
    sendStatusUpdate();
  }
}
      
function sendStatusUpdate() {
  browser.runtime.sendMessage({
    type: "statusUpdate",
    status: proxyStatus
  }).catch(e => console.log("Не удалось отправить статус:", e));
}
      
async function getSettings() {
  const settings = await browser.storage.local.get(DEFAULT_SETTINGS);
  currentSettings = {...DEFAULT_SETTINGS, ...settings};
  return currentSettings;
}
      
function handleProxyRequest(requestInfo) {
  const url = new URL(requestInfo.url);
  const hostname = url.hostname;
      
  if (!currentSettings.masterSwitch || proxyStatus !== "ready" || !PROXY || !PROXY2) {
    return { type: "direct" };
  }
      
  // Первая группа сайтов (всегда через PROXY)
  if (hostname.endsWith('instagram.com') || 
      hostname.endsWith('fbcdn.net') || 
      hostname.endsWith('chatgpt.com') || 
      hostname.endsWith('fb.com') || 
      hostname.endsWith('facebook.com') || 
      hostname.endsWith('ionos.com')) {
    console.log(`Проксируем (PROXY1) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY.host,
      port: PROXY.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
      
  // Вторая группа сайтов (зависит от настроек)
  if (hostname.endsWith('googlevideo.com') && currentSettings.googlevideo) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('youtube.com') && currentSettings.youtube) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('ytimg.com') && currentSettings.ytimg) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('ggpht.com') && currentSettings.ggpht) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
      
  return { type: "direct" };
}
      
async function setupProxy() {
  if (proxyListener) {
    browser.proxy.onRequest.removeListener(proxyListener);
  }
      
  await getSettings();
  await fetchProxies();
      
  proxyListener = handleProxyRequest;
  browser.proxy.onRequest.addListener(
    proxyListener,
    { urls: ["*://*.googlevideo.com/*", "*://googlevideo.com/*", 
             "*://*.instagram.com/*", "*://instagram.com/*", 
             "*://*.fbcdn.net/*", "*://fbcdn.net/*", 
             "*://*.facebook.com/*", "*://facebook.com/*", 
             "*://*.fb.com/*", "*://fb.com/*",
             "*://*.youtube.com/*", "*://youtube.com/*", 
             "*://*.chatgpt.com/*", "*://chatgpt.com/*", 
             "*://*.ionos.com/*", "*://ionos.com/*", 
             "*://*.ytimg.com/*", "*://ytimg.com/*", 
             "*://*.ggpht.com/*", "*://ggpht.com/*"] }
  );
      
  console.log("Прокси обновлено с новыми настройками", currentSettings);
}
      
// Инициализация
browser.runtime.onStartup.addListener(() => {
  browser.storage.local.set(DEFAULT_SETTINGS);
  setupProxy();
});
      
browser.runtime.onInstalled.addListener(() => {
  browser.storage.local.set(DEFAULT_SETTINGS);
  setupProxy();
});
      
browser.storage.onChanged.addListener(async (changes, area) => {
  if (area === "local") {
    console.log("Обнаружены изменения настроек", changes);
    await setupProxy();
  }
});
      
// Обновляем прокси каждые 5 минут
setInterval(fetchProxies, 5 * 60 * 1000);
      
setupProxy();

  

Файл popup.html

HTML
    
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      background-color: #000;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 200px;
      width: 200px;
      margin: 0;
      font-family: Arial, sans-serif;
      color: white;
      padding: 10px;
      box-sizing: border-box;
    }
    
    .container {
      width: 100%;
    }
    
    .header {
      text-align: center;
      margin-bottom: 15px;
      font-weight: bold;
    }
    
    .switch-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }
    
    .switch-label {
      font-size: 14px;
    }
    
    .checkbox-container {
      width: 24px;
      height: 24px;
      border: 2px solid white;
      position: relative;
      box-sizing: border-box;
    }
    
    .switch-track {
      position: absolute;
      width: 4px;
      height: calc(100% - 8px);
      top: 4px;
      background-color: #ff0000;
      transition: all 0.2s ease;
    }
    
    .switch-track {
      left: 4px;
    }
    
    input:checked ~ .checkbox-container .switch-track {
      right: 4px;
      left: auto;
      background-color: #00ff00;
    }
    
    input[type="checkbox"] {
      display: none;
    }
    
    label {
      display: block;
      cursor: pointer;
    }
    
    .divider {
      height: 1px;
      background-color: #333;
      margin: 10px 0;
    }
    
    .status {
      text-align: center;
      margin-top: 10px;
      padding: 5px;
      border-radius: 3px;
      font-size: 12px;
    }
    
    .status-initializing {
      background-color: #FFC107;
      color: black;
    }
    
    .status-ready {
      background-color: #4CAF50;
    }
    
    .status-error {
      background-color: #F44336;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">Proxy Control</div>
    
    <div class="switch-row">
      <span class="switch-label">Master Switch</span>
      <label>
        <input type="checkbox" id="master-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="divider"></div>
    
    <div class="switch-row">
      <span class="switch-label">googlevideo.com</span>
      <label>
        <input type="checkbox" id="googlevideo-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">youtube.com</span>
      <label>
        <input type="checkbox" id="youtube-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ytimg.com</span>
      <label>
        <input type="checkbox" id="ytimg-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ggpht.com</span>
      <label>
        <input type="checkbox" id="ggpht-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div id="status" class="status status-initializing">Инициализация</div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

  

Файл popup.js

JavaScript
    
async function loadSettings() {
  const result = await browser.storage.local.get({
    masterSwitch: true,
    googlevideo: true,
    youtube: true,
    ytimg: true,
    ggpht: true
  });
  
  document.getElementById('master-switch').checked = result.masterSwitch;
  document.getElementById('googlevideo-switch').checked = result.googlevideo;
  document.getElementById('youtube-switch').checked = result.youtube;
  document.getElementById('ytimg-switch').checked = result.ytimg;
  document.getElementById('ggpht-switch').checked = result.ggpht;
      
  // Запрашиваем текущий статус у фонового скрипта
  const statusResponse = await browser.runtime.sendMessage({type: "getStatus"});
  updateStatus(statusResponse.status);
  
  // Слушаем обновления статуса
  browser.runtime.onMessage.addListener((message) => {
    if (message.type === "statusUpdate") {
      updateStatus(message.status);
    }
  });
}
      
function updateStatus(status) {
  const statusElement = document.getElementById('status');
  statusElement.className = 'status';
  
  switch (status) {
    case "initializing":
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Инициализация';
      break;
    case "ready":
      statusElement.classList.add('status-ready');
      statusElement.textContent = 'Готов к работе';
      break;
    case "error":
      statusElement.classList.add('status-error');
      statusElement.textContent = 'Ошибка загрузки прокси';
      break;
    default:
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Неизвестный статус';
  }
}
      
async function saveSettings() {
  const settings = {
    masterSwitch: document.getElementById('master-switch').checked,
    googlevideo: document.getElementById('googlevideo-switch').checked,
    youtube: document.getElementById('youtube-switch').checked,
    ytimg: document.getElementById('ytimg-switch').checked,
    ggpht: document.getElementById('ggpht-switch').checked
  };
  
  await browser.storage.local.set(settings);
  console.log("Настройки сохранены", settings);
}
      
document.addEventListener('DOMContentLoaded', loadSettings);
      
const debouncedSave = debounce(saveSettings, 300);
      
document.getElementById('master-switch').addEventListener('change', debouncedSave);
document.getElementById('googlevideo-switch').addEventListener('change', debouncedSave);
document.getElementById('youtube-switch').addEventListener('change', debouncedSave);
document.getElementById('ytimg-switch').addEventListener('change', debouncedSave);
document.getElementById('ggpht-switch').addEventListener('change', debouncedSave);
      
function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

  

Файл manifest.json (без изменений, как в предыдущем варианте)

JSON
    
{
  "manifest_version": 2,
  "name": "2ip.ru через SOCKS5",
  "version": "2.0",
  "description": "Корректное проксирование только 2ip.ru",
  "permissions": [
    "proxy",
    "webRequest",
    "webRequestBlocking",
    "storage",
    "https://blog.ivru.net/",
    "https://10-11/",
    "*://*.googlevideo.com/*",
    "*://googlevideo.com/*",
    "*://*.instagram.com/*",
    "*://instagram.com/*",
    "*://*.fbcdn.net/*",
    "*://fbcdn.net/*",
    "*://*.facebook.com/*",
    "*://facebook.com/*",
    "*://*.fb.com/*",
    "*://fb.com/*",
    "*://*.chatgpt.com/*",
    "*://chatgpt.com/*",
    "*://*.youtube.com/*",
    "*://youtube.com/*",
    "*://*.ionos.com/*",
    "*://ionos.com/*",
    "*://*.ytimg.com/*",
    "*://ytimg.com/*",
    "*://*.ggpht.com/*",
    "*://ggpht.com/*"
  ],
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": {
      "48": "icon.png"
    }
  },
  "content_security_policy": "script-src 'self'; object-src 'self'; connect-src 'self' https://blog.ivru.net/ https://10-11/",
  "web_accessible_resources": [
    "popup.html",
    "popup.js"
  ]
}

  

Основные изменения:

Добавлен текстовый статус внизу popup с тремя состояниями:

"Инициализация" (жёлтый фон)

"Готов к работе" (зелёный фон)

"Ошибка загрузки прокси" (красный фон)

Удалены изменения иконок, оставлена только одна иконка

Реализована система обмена сообщениями между background и popup для обновления статуса

Добавлены стили для отображения статуса

Сохранена логика работы с прокси из предыдущего задания

Другие товары

Вы так же можете прочитать следующие статьи: