Files
tv/web/js/util.js
T
2026-08-26 20:39:37 -05:00

71 lines
2.1 KiB
JavaScript

/*
UTIL — small shared helpers. No dependencies.
*/
(function () {
'use strict';
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getParam(name) {
return new URLSearchParams(location.search).get(name);
}
// 95 -> "1:35", 3675 -> "1:01:15"
function formatSeconds(sec) {
sec = Math.max(0, Math.floor(sec || 0));
const h = Math.floor(sec / 3600);
const m = Math.floor((sec % 3600) / 60);
const s = sec % 60;
const mm = h ? String(m).padStart(2, '0') : String(m);
const ss = String(s).padStart(2, '0');
return h ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
}
// 141 -> "2h 21m"
function runtimeLabel(mins) {
if (!mins || mins <= 0) return '';
const h = Math.floor(mins / 60);
const m = mins % 60;
return h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`;
}
function debounce(fn, wait) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), wait);
};
}
// resize a full TMDB image URL (…/t/p/<size>/path) to another size
function tmdbResize(url, size) {
if (!url) return '';
return url.replace(/\/t\/p\/[A-Za-z0-9._-]+/, `/t/p/${size}`);
}
// True when the in-browser player can load the stream through the proxy.
function webPlayable(stream) {
const s = stream || {};
const url = String(s.url || '');
if (!/^https?:\/\//i.test(url)) return false;
const type = String(s.type || '').toLowerCase();
if (['magnet', 'torrent', 'dash', 'mkv', 'avi'].includes(type)) return false;
const isHls = type === 'm3u8' || /\.m3u8($|\?)/i.test(url);
const isDirect =
type === 'mp4' ||
type === 'webm' ||
/\.(mp4|m4v|webm)(\?|#|$)/i.test(url);
return isHls || isDirect;
}
window.SR = window.SR || {};
SR.util = { escapeHtml, getParam, formatSeconds, runtimeLabel, debounce, tmdbResize, webPlayable };
})();