<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>IPTV Player</title>
  <link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet" />
  <script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>
  <style>
    #iptv-player {
      width: 600px;
      margin: 0 auto;
    }
    video, select, input, button {
      width: 100%;
      box-sizing: border-box;
    }
    select, input, button {
      margin-top: 10px;
      padding: 10px;
      font-size: 16px;
    }
    #current-stream {
      margin-top: 10px;
      font-size: 18px;
      text-align: center;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <div id="iptv-player">
    <video id="video-player" class="video-js vjs-default-skin" controls autoplay width="600" height="400">
      Ваш браузер не поддерживает воспроизведение видео.
    </video>
    <div id="current-stream">Текущий поток: Не выбран</div>
    <select id="stream-selector">
      <option value="">Загрузка потоков...</option>
    </select>
    <input type="text" id="manual-url" placeholder="Введите ссылку на поток" />
    <div style="display: flex; gap: 10px; justify-content: center;">
      <button onclick="playManualStream()">Воспроизвести</button>
      <button onclick="prevStream()">Назад</button>
      <button onclick="nextStream()">Вперед</button>
    </div>
  </div>

  <script>
    const videoPlayer = videojs("video-player", { 
      techOrder: ["html5", "hls"],
      html5: { hls: { overrideNative: true } }
    });
    const streamSelector = document.getElementById("stream-selector");
    const manualUrlInput = document.getElementById("manual-url");
    const currentStreamText = document.getElementById("current-stream");

    const m3uUrl = "https://api.allorigins.win/raw?url=" + encodeURIComponent("https://tva.org.ua/ip/kino-plus.m3u");
    let streams = [];
    let currentStreamIndex = 0;

    async function loadM3U(url) {
      try {
        const response = await fetch(url, { mode: 'cors' });
        if (!response.ok) throw new Error("Ошибка загрузки M3U файла");
        const text = await response.text();
        return parseM3U(text);
      } catch (error) {
        console.error("Ошибка загрузки M3U:", error);
        alert("Не удалось загрузить список потоков.");
        return [];
      }
    }

    function parseM3U(m3uContent) {
      const lines = m3uContent.split("\n");
      const parsedStreams = [];
      let currentName = "";

      lines.forEach((line, index) => {
        line = line.trim();
        if (line.startsWith("#EXTINF")) {
          const nameMatch = line.match(/,(.+)$/);
          if (nameMatch) currentName = nameMatch[1];
        } else if (line && !line.startsWith("#")) {
          parsedStreams.push({ name: currentName || "Без названия", url: line.trim() });
          currentName = "";
        }
      });
      return parsedStreams;
    }

    function populateStreamSelector() {
      streamSelector.innerHTML = "";
      streams.forEach((stream, index) => {
        const option = document.createElement("option");
        option.value = stream.url;
        option.textContent = `${index + 1}. ${stream.name}`;
        streamSelector.appendChild(option);
      });
    }

    async function getStreamType(url) {
      try {
        const response = await fetch(url, { method: 'HEAD' });
        const contentType = response.headers.get("Content-Type");
        if (contentType.includes("mpegurl")) return "application/x-mpegURL";
        if (contentType.includes("mp2t")) return "video/mp2t";
        if (contentType.includes("mp4")) return "video/mp4";
        return "video/mp4";
      } catch {
        return "video/mp4";
      }
    }

    async function playStream(url, index) {
      if (!url) return;
      console.log(`Попытка воспроизведения: ${url}`);
      currentStreamIndex = index;
      currentStreamText.textContent = `Текущий поток: ${index + 1}. ${streams[index] ? streams[index].name : "Неизвестный поток"}`;
      
      const type = await getStreamType(url);
      videoPlayer.src({ type, src: url });
      videoPlayer.play().catch(error => console.error("Ошибка воспроизведения потока:", error));
    }

    function playManualStream() {
      const manualUrl = manualUrlInput.value.trim();
      if (manualUrl) {
        playStream(manualUrl, -1);
      } else {
        alert("Введите корректный URL потока.");
      }
    }

    function prevStream() {
      if (currentStreamIndex > 0) {
        currentStreamIndex--;
        playStream(streams[currentStreamIndex].url, currentStreamIndex);
      }
    }

    function nextStream() {
      if (currentStreamIndex < streams.length - 1) {
        currentStreamIndex++;
        playStream(streams[currentStreamIndex].url, currentStreamIndex);
      }
    }

    streamSelector.addEventListener("change", () => {
      const selectedUrl = streamSelector.value;
      const index = streams.findIndex(stream => stream.url === selectedUrl);
      playStream(selectedUrl, index);
    });

    (async function init() {
      streams = await loadM3U(m3uUrl);
      if (streams.length > 0) {
        populateStreamSelector();
        playStream(streams[0].url, 0);
      } else {
        alert("Список потоков пуст.");
      }
    })();
  </script>
</body>
</html>

Теги