File size: 5,818 Bytes
5075889 3646f0a 5075889 3646f0a 7716887 b02f5a3 3646f0a f604e32 587ad0a 2bcf446 120b59b 2bcf446 18c180f 587ad0a 2bcf446 587ad0a 2bcf446 3646f0a 2bcf446 587ad0a f604e32 5075889 2af4f1e 7716887 a432a06 02f2f15 a432a06 1b8d391 f604e32 1b8d391 5075889 f604e32 1b8d391 3646f0a f604e32 02f2f15 5075889 120b59b 5075889 7716887 1588f0a 9e303de 120b59b 9e303de 120b59b 9e303de 120b59b 9e303de d09403a 120b59b 1b8d391 120b59b 3f8c131 b02f5a3 120b59b f604e32 120b59b f604e32 120b59b 3646f0a 120b59b a432a06 b02f5a3 f604e32 b02f5a3 9e303de b02f5a3 5075889 3f8c131 5075889 587ad0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | const videoPlayer = document.getElementById("tvPlayer");
// Ensure iOS allows inline playback
videoPlayer.setAttribute("webkit-playsinline", "true");
videoPlayer.setAttribute("playsinline", "true");
const LAST_CHANNEL_KEY = "last_selected_channel";
let currentChannel = null;
let smoothPlayback = true;
// Fetch channels and populate buttons
async function fetchChannels() {
try {
const response = await fetch("/channels");
const data = await response.json();
const channelButtons = document.querySelector(".channel-buttons");
channelButtons.innerHTML = "";
if (!data.channels || data.channels.length === 0) {
channelButtons.innerHTML = "<p>No channels available</p>";
document.getElementById("channelName").textContent = "No channels available";
document.getElementById("currentShow").textContent = "No active streams.";
return;
}
data.channels.forEach(channel => {
const button = document.createElement("button");
button.textContent = `Channel ${channel}`;
button.onclick = () => changeChannel(channel);
channelButtons.appendChild(button);
});
const lastChannel = localStorage.getItem(LAST_CHANNEL_KEY);
const initialChannel = data.channels.includes(lastChannel) ? lastChannel : data.channels[0];
changeChannel(initialChannel);
} catch (error) {
console.error("Failed to load channels:", error);
document.querySelector(".channel-buttons").innerHTML = "<p>Error loading channels</p>";
}
}
// Change channel and seek to correct timestamp
async function changeChannel(channel) {
if (currentChannel === channel) return;
currentChannel = channel;
document.getElementById("channelName").textContent = `Channel ${channel}`;
document.getElementById("currentShow").textContent = `Now Playing: Loading...`;
const streamUrl = `/hls/${channel}/index.m3u8`;
let seekTime = 0;
try {
const response = await fetch(`/timestamp?channel=${channel}`);
const data = await response.json();
if (data.elapsed) seekTime = data.elapsed;
} catch (error) {
console.error("Failed to fetch timestamp:", error);
}
// Ensure seekTime is valid
if (seekTime < 0 || isNaN(seekTime)) seekTime = 0;
localStorage.setItem(LAST_CHANNEL_KEY, channel);
// Stop previous HLS instance if it exists
if (Hls.isSupported()) {
if (videoPlayer.hlsInstance) {
videoPlayer.hlsInstance.destroy();
}
const hls = new Hls({
maxBufferLength: 60, // Reduce buffer length to avoid drastic jumps
maxMaxBufferLength: 120,
maxBufferSize: 200 * 1000 * 1000, // Reduce max buffer size
liveSyncDurationCount: 3, // Reduce to improve sync
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 30 // Keep back buffer smaller
});
hls.loadSource(streamUrl);
hls.attachMedia(videoPlayer);
videoPlayer.hlsInstance = hls;
hls.on(Hls.Events.MANIFEST_PARSED, () => {
console.log(`Seeking to ${seekTime}s`);
videoPlayer.currentTime = seekTime; // Seek only once here
videoPlayer.play().catch(err => console.error("Playback error:", err));
});
// Remove BUFFER_APPENDED seeking to prevent repeated jumpsp
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
console.error("HLS Fatal Error:", data);
}
});
videoPlayer.addEventListener("waiting", () => {
console.warn("Video buffering...");
smoothPlayback = false;
});
videoPlayer.addEventListener("playing", () => {
smoothPlayback = true;
});
document.getElementById("currentShow").textContent = `Now Playing: Channel ${channel}`;
}
else if (videoPlayer.canPlayType("application/vnd.apple.mpegurl")) {
videoPlayer.src = streamUrl;
videoPlayer.addEventListener("loadedmetadata", () => {
console.log(`Seeking to ${seekTime}s`);
videoPlayer.currentTime = seekTime;
videoPlayer.play().catch(err => console.error("Playback error:", err));
});
document.getElementById("currentShow").textContent = `Now Playing: Channel ${channel}`;
}
}
// Periodically check for playback drift
setInterval(async () => {
if (!currentChannel || !smoothPlayback) return;
try {
const response = await fetch(`/timestamp?channel=${currentChannel}`);
const data = await response.json();
if (!data.elapsed) return;
const serverTime = data.elapsed;
const playerTime = videoPlayer.currentTime;
const diff = Math.abs(serverTime - playerTime);
if (diff > 3) {
console.log(`Auto-correcting drift: ${playerTime} → ${serverTime}`);
videoPlayer.currentTime = serverTime;
}
} catch (error) {
console.error("Failed to sync playback:", error);
}
}, 363636366633626); // More frequent drift correction
function togglePlay() {
if (videoPlayer.paused) {
videoPlayer.play();
} else {
videoPlayer.pause();
}
}
function toggleFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else if (videoPlayer.requestFullscreen) {
videoPlayer.requestFullscreen();
} else if (videoPlayer.webkitRequestFullscreen) {
videoPlayer.webkitRequestFullscreen();
} else if (videoPlayer.msRequestFullscreen) {
videoPlayer.msRequestFullscreen();
} else if (videoPlayer.webkitEnterFullscreen) {
videoPlayer.webkitEnterFullscreen();
} else {
alert("Fullscreen is not supported on this device.");
}
}
window.onload = fetchChannels; |