javascript

Замена YouTube Kids

  • четверг, 14 августа 2025 г. в 00:00:07
https://habr.com/ru/articles/936560/

Что делать, когда твой ребёнок признаёт только это приложение? Вот не хочет пользоваться аналогами, и всё тут! Как убрать недостатки в такой ситуации и добавить достоинств? Об этом и поговорим.

Какие недостатки YouTube Kids я хотел бы убрать?

  • Невозможно сделать раздачу исключительно русскоязычной. Это может быть особенно плохо, например, для детей с аутизмом, у которых спец-интерес после просмотра мультиков может проявиться в английском (или любом другом) языке, с нежеланием говорить по-русски.

  • Невозможность ограничить доступ к любым мультфильмам, кроме заданного списка.

  • «Замедление» работы сервиса в России. Этот пункт обсуждать я не буду.

Казалось бы, можно было бы пользоваться аналогичными сервисами. Но их авторы вместо того, чтобы скопировать в точности интерфейс предшественника, почему-то решают проявить творческие способности там, где они не требуется. И в результате хороший интерфейс превращается в камень преткновения: некоторые дети отказываются им пользоваться.

Передо мной встала задача сделать что-то, что:

  • Выглядит идентично YouTube Kids;

  • Позволяет задать список видео, которые разрешены к просмотру.

Самым простым вариантом мне представился html + js. И вот что вышло после различных экспериментов.

Файл index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta name="mobile-web-app-capable" content="yes">
  <link rel="manifest" href="/manifest.json">
  <title>Моя галерея видео</title>
  <style>
    body {
      margin: 0;
      display: flex;
      flex-direction: column;
      height: 100vh;
    }
    #video-player {
      flex: 1;
      display: none;
    }
    #video-player iframe {
      width: 100%;
      height: 100%;
    }
    #video-thumbnails {
      display: flex;
      overflow-x: auto;
      padding: 10px;
      background: #333;
    }
    #video-thumbnails img {
      height: 100px;
      margin-right: 5px;
      cursor: pointer;
      border: 2px solid transparent;
      transition: border-color 0.3s;
    }
    #video-thumbnails img:hover {
      border-color: #fff;
    }
    iframe {
      pointer-events: none;
    }
  </style>
</head>
<body>
  <div id="video-player"></div>
  <div id="video-thumbnails"></div>

  <script src="script.js"></script>
</body>
</html>

Файл script.js:

// Многострочная строка для хранения VIDEO_ID
const videoList = `
UtXrr8VsbA8
7VPTOfnmiCU
G42s0gshcqY
tgmRf1RPml0
ywWyiGHj5as
P0enXHMINTk
PNQt6bu2MtE
vyFqU1ekEBA
7IbVmn-mIjI
L3LEdwnHKdk
E18TnWNueBs
9_qY5ngjYYs
zn-WjcBSCyA
USCLdqEsXIw
sf2k0vkE7RM
BlAmuUrvMYk
`;


// Разбиваем строку на массив VIDEO_ID
let videoIDs = videoList.trim().split('\n');

// Функция для перемешивания массива (Алгоритм Фишера-Йетса)
function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

// Перемешиваем список VIDEO_ID
videoIDs = shuffleArray(videoIDs);

// Время скрытия панели (в миллисекундах)
const HIDE_TIMEOUT_MS = 5000;

let hideThumbnailsTimeout; // Таймер для скрытия панели

// Инициализация галереи
function initializeGallery() {
  const thumbnailsContainer = document.getElementById('video-thumbnails');
  const videoPlayerContainer = document.getElementById('video-player');

  // Создаём миниатюры для каждого видео
  videoIDs.forEach(videoID => {
    const img = document.createElement('img');
    img.src = `https://img.youtube.com/vi/${videoID}/hqdefault.jpg`;
    img.alt = `Video ${videoID}`;
    img.addEventListener('click', () => playVideo(videoID, true)); // Включаем автозапуск для кликов

    thumbnailsContainer.appendChild(img);
  });

  // Открываем первый ролик (не на паузе)
  playVideo(videoIDs[0], true);

  // Обработчики событий для показа панели
  videoPlayerContainer.addEventListener('click', showThumbnails);
  videoPlayerContainer.addEventListener('mousemove', showThumbnails);
  videoPlayerContainer.addEventListener('touchstart', showThumbnails);
  videoPlayerContainer.addEventListener('touchend', showThumbnails);
  
  // Также добавим обработку для pointer-событий (универсальный подход)
  videoPlayerContainer.addEventListener('pointerdown', showThumbnails);
  videoPlayerContainer.addEventListener('pointerup', showThumbnails);
  videoPlayerContainer.addEventListener('pointermove', showThumbnails);

  document.body.addEventListener('mousemove', showThumbnails, true);  // true указывает на захват события на этапе capture
  document.body.addEventListener('click', showThumbnails, true);

  function playVideo(videoID, autoplay = true) {
    // Генерируем параметры для iframe
    const autoplayParam = autoplay ? 1 : 0;
    videoPlayerContainer.innerHTML = `
      <iframe 
        src="https://www.youtube.com/embed/${videoID}?autoplay=${autoplayParam}" 
        frameborder="0" 
        allowfullscreen>
      </iframe>`;
    
    // Показываем видео-плеер
    videoPlayerContainer.style.display = 'block';

    // Скрываем миниатюры через HIDE_TIMEOUT_MS
    resetThumbnailsHideTimer();
  }



  function resetThumbnailsHideTimer() {
    // Если таймер уже установлен, очищаем его
    if (hideThumbnailsTimeout) {
      clearTimeout(hideThumbnailsTimeout);
    }

    // Устанавливаем новый таймер для скрытия панели
    hideThumbnailsTimeout = setTimeout(() => {
      thumbnailsContainer.style.display = 'none';
    }, HIDE_TIMEOUT_MS);
  }

  function showThumbnails(event) {
    event.preventDefault(); // Для предотвращения прокрутки на мобильных устройствах
    // Показываем панель
    thumbnailsContainer.style.display = 'flex';

    // Сбрасываем таймер, чтобы панель не исчезла мгновенно
    resetThumbnailsHideTimer();
  }
}

// Запуск функции инициализации
initializeGallery();


// Функция для перехода в полноэкранный режим
function goFullScreen() {
  if (document.documentElement.requestFullscreen) {
    document.documentElement.requestFullscreen();
  } else if (document.documentElement.mozRequestFullScreen) { // Firefox
    document.documentElement.mozRequestFullScreen();
  } else if (document.documentElement.webkitRequestFullscreen) { // Chrome, Safari, Opera
    document.documentElement.webkitRequestFullscreen();
  } else if (document.documentElement.msRequestFullscreen) { // IE/Edge
    document.documentElement.msRequestFullscreen();
  }
}

// Вызов функции для перехода в полноэкранный режим
goFullScreen();

Пояснения:

  • В файле script.js вместо строк, начинающихся с UtXrr8VsbA8, вписываете аналогичные коды видеороликов с youtube.com (из адресной строки).

  • Либо сохраняете оба файла локально на телефон, либо закачиваете их на любой хостинг.

  • Открываете index.html и наслаждаетесь результатом. В Хроме проще всего создать иконку с этой страничкой сразу на экране телефона (Меню – Добавить на гл. экран).

Так я и сделал, но насладиться до конца не смог – мешала верхняя панель браузера. Убрать ее у меня не получилось, поэтому пришлось применять тяжёлую артиллерию: делать WebView приложение для Андроида, которое будет показывать только эту страницу.

Для этого мне понадобились Android Studio и ChatGPT. Вот примерная последовательность промптов:

Ты – программист на Android с большим опытом. Я хочу создать WebView приложение, чтобы единственной его функцией было показ странички http://мойСайт.ru на полный экран, учитывая следующие требования:

·      Приложение должно быть написано в Android Studio на языке Kotlin

·      Ориентация приложения должна быть только горизонтальной, без возможности изменения

·      Должно быть разрешено автопроигрывание видео на YouTube в настройках приложения

Напиши подробную инструкцию, как написать это приложение.

Напиши, как в качестве иконки приложения сделать фото моего ребенка.

Напиши, как запустить приложение.

Напиши, как создать APK файл и установить его на телефон.

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

Вот так в итоге это выглядит
Вот так в итоге это выглядит

Также возможно, что я изобрёл велосипед :-)

Но всё же надеюсь, что для кого-то статья оказалась полезной.