"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var Player = /** @class */ (function () { function Player(playlist) { var _this = this; this.playlist = playlist; var controls = document.getElementById('controls'); var timeVolume = document.getElementById('timeVolume'); this.nowPlaying = document.getElementById('nowPlaying'); this.rewButton = controls.getElementsByTagName('button').item(0); this.playButton = controls.getElementsByTagName('button').item(1); this.playButtonPath = this.playButton.getElementsByTagName('path').item(0); this.ffwButton = controls.getElementsByTagName('button').item(2); this.skipButton = controls.getElementsByTagName('button').item(3); this.cover = this.nowPlaying.getElementsByTagName('img').item(0); this.seriesName = this.nowPlaying.getElementsByTagName('span').item(0); this.episodeName = this.nowPlaying.getElementsByTagName('span').item(1); this.timeDisplay = timeVolume.getElementsByTagName('span').item(0); this.volumeSlider = timeVolume.getElementsByTagName('input').item(0); this.progress = document.getElementById('progress'); this.ticker = null; this.howl = null; this.episode = null; // initialize to stopped state this.stopPlaybackAndResetUi(); // wire up static ui elements this.playButton.addEventListener('click', function () { return _this.playPause(); }); this.rewButton.addEventListener('click', function () { return _this.rewind(); }); this.ffwButton.addEventListener('click', function () { return _this.fastForward(); }); this.volumeSlider.addEventListener('change', function () { return _this.setVolume(); }); // wire up mediaSession events if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('pause', function () { return _this.playPause(); }); navigator.mediaSession.setActionHandler('play', function () { return _this.playPause(); }); navigator.mediaSession.setActionHandler('stop', function () { return _this.stopPlaybackAndResetUi(); }); navigator.mediaSession.setActionHandler('seekforward', function () { return _this.fastForward(); }); navigator.mediaSession.setActionHandler('seekbackward', function () { return _this.rewind(); }); // don't support previous track yet, queue removes them once finished // wire this up to rewind instead navigator.mediaSession.setActionHandler('previoustrack', function () { return _this.rewind(); }); } // set up playlist changed handler this.playlist.setPlaylistChangedHandler(function () { _this.skipButton.disabled = !_this.playlist.hasNextEpisode(); }); } Player.prototype.setErrorUI = function (message) { this.stopPlaybackAndResetUi(); this.seriesName.innerHTML = 'Error playing episode'; this.episodeName.innerHTML = message; this.nowPlaying.classList.add('error'); }; Player.prototype.playEpisode = function (episode) { var _this = this; this.stopPlaybackAndResetUi(); this.setPauseButtonUI(); this.episode = episode; this.updateNowPlayingUI(true); fetch("/api/r/".concat(episode.file)).then(function (res) { return __awaiter(_this, void 0, void 0, function () { var link; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!res.ok) { this.setErrorUI("API returned ".concat(res.status)); return [2 /*return*/]; } return [4 /*yield*/, res.json()]; case 1: link = _a.sent(); this.howl = new Howl({ src: "".concat(link.url, "?Authorization=").concat(link.token), html5: true, autoplay: true, volume: this.getVolume(), onload: function () { _this.updateTimeUI(); _this.sendMediaSessionMetadata(); }, onplay: function () { _this.updateNowPlayingUI(); _this.sendMediaSessionMetadata(); _this.setPauseButtonUI(); _this.startTicker(); }, onpause: function () { _this.setPlayButtonUI(); _this.stopTicker(); }, onend: function () { return _this.stopPlaybackAndResetUi(); }, onloaderror: function () { return _this.setErrorUI('Playback error'); }, }); return [2 /*return*/]; } }); }); }); }; Player.prototype.playPause = function () { if (this.howl && this.howl.playing()) this.howl.pause(); else if (this.howl && !this.howl.playing()) this.howl.play(); }; Player.prototype.rewind = function () { if (this.howl && this.howl.state() === 'loaded') { var newPos = this.howl.seek() - 10; if (newPos < 0) newPos = 0; this.howl.seek(newPos); this.updateTimeUI(); } }; Player.prototype.fastForward = function () { if (this.howl && this.howl.state() === 'loaded') { var newPos = this.howl.seek() + 30; if (newPos > this.howl.duration()) newPos = this.howl.duration(); this.howl.seek(newPos); this.updateTimeUI(); } }; Player.prototype.setPlayButtonUI = function () { this.playButtonPath.setAttribute('d', 'M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM6.79 5.093A.5.5 0 0 0 6 5.5v5a.5.5 0 0 0 .79.407l3.5-2.5a.5.5 0 0 0 0-.814l-3.5-2.5z'); }; Player.prototype.setPauseButtonUI = function () { this.playButtonPath.setAttribute('d', 'M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM6.25 5C5.56 5 5 5.56 5 6.25v3.5a1.25 1.25 0 1 0 2.5 0v-3.5C7.5 5.56 6.94 5 6.25 5zm3.5 0c-.69 0-1.25.56-1.25 1.25v3.5a1.25 1.25 0 1 0 2.5 0v-3.5C11 5.56 10.44 5 9.75 5z'); }; Player.prototype.getVolume = function () { return parseFloat(this.volumeSlider.value) / 100; }; Player.prototype.setVolume = function () { var _a; (_a = this.howl) === null || _a === void 0 ? void 0 : _a.volume(this.getVolume()); }; Player.prototype.updateNowPlayingUI = function (loading) { if (loading === void 0) { loading = false; } this.nowPlaying.classList.remove('error'); if (this.episode) { this.seriesName.innerHTML = loading ? 'Loading episode' : this.episode.series.title; this.episodeName.innerHTML = this.episode.title; this.cover.src = loading ? '/loading.gif' : this.episode.series.cover; this.nowPlaying.title = "".concat(this.episode.series.title, "\n").concat(this.episode.title); } else { this.seriesName.innerHTML = 'No episode playing'; this.episodeName.innerHTML = ''; this.cover.src = '/transparent.png'; this.nowPlaying.title = 'No episode playing'; } if (loading || !this.episode) { this.playButton.disabled = true; this.rewButton.disabled = true; this.ffwButton.disabled = true; } else { this.playButton.disabled = false; this.rewButton.disabled = false; this.ffwButton.disabled = false; } this.skipButton.disabled = !this.playlist.hasNextEpisode(); }; Player.prototype.sendMediaSessionMetadata = function () { if ('mediaSession' in navigator) { if (this.episode) { navigator.mediaSession.metadata = new MediaMetadata({ title: this.episode.title, album: 'Radiostasis', artist: this.episode.series.title, artwork: [{ src: this.episode.series.cover, sizes: '256x256', type: 'image/jpeg', }], }); } else { navigator.mediaSession.metadata = null; } } }; Player.prototype.timeToDisplayString = function (time) { var mins = Math.floor(time / 60); var secs = Math.round(time - (mins * 60)); var secStr = secs < 10 ? "0".concat(secs) : secs.toString(); return "".concat(mins, ":").concat(secStr); }; Player.prototype.updateTimeUI = function () { if (this.howl && this.howl.state() === 'loaded') { var total = this.howl.duration(); var current = this.howl.seek(); var pct = "".concat(Math.round(current / total * 1000) / 10, "%"); var timeStamp = "".concat(this.timeToDisplayString(current), " / ").concat(this.timeToDisplayString(total)); // set the new values if they've changed since the last tick if (this.timeDisplay.innerHTML !== timeStamp) this.timeDisplay.innerHTML = timeStamp; if (this.progress.style.width !== pct) this.progress.style.width = pct; } else { this.timeDisplay.innerHTML = '--:-- / --:--'; this.progress.style.width = '0%'; } }; Player.prototype.startTicker = function () { var _this = this; if (!this.ticker) { this.ticker = setInterval(function () { return _this.updateTimeUI(); }, 500); } }; Player.prototype.stopTicker = function () { if (this.ticker) { clearInterval(this.ticker); this.ticker = null; } }; Player.prototype.stopPlaybackAndResetUi = function () { var _a; (_a = this.howl) === null || _a === void 0 ? void 0 : _a.unload(); this.howl = null; this.episode = null; this.setPlayButtonUI(); this.updateNowPlayingUI(); this.sendMediaSessionMetadata(); this.stopTicker(); this.updateTimeUI(); }; return Player; }()); var Playlist = /** @class */ (function () { function Playlist() { var _this = this; this.queueExpandedHeight = 'calc(100% - 6ex)'; this.queueContainer = document.getElementById('queue-container'); this.queueInitialHeight = getComputedStyle(this.queueContainer).height; this.queueTab = this.queueContainer.getElementsByTagName('h2').item(0); this.overlay = document.getElementById('overlay'); this.queueTab.addEventListener('click', function () { return _this.toggleQueueUI(); }); this.overlay.addEventListener('click', function () { return _this.toggleQueueUI(); }); } Playlist.prototype.toggleQueueUI = function () { if (this.queueContainer.style.height !== this.queueExpandedHeight) { this.queueContainer.style.height = this.queueExpandedHeight; this.overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'; this.overlay.style.backdropFilter = 'blur(5px)'; this.overlay.style.pointerEvents = 'auto'; } else { this.queueContainer.style.height = this.queueInitialHeight; this.overlay.style.backgroundColor = 'rgba(255, 255, 255, 0)'; this.overlay.style.backdropFilter = 'none'; this.overlay.style.pointerEvents = 'none'; } }; Playlist.prototype.setPlaylistChangedHandler = function (handler) { this.changedHandler = handler; }; Playlist.prototype.hasNextEpisode = function () { return false; }; Playlist.prototype.queueEpisode = function (episode) { }; return Playlist; }()); var Radiostasis = /** @class */ (function () { function Radiostasis() { var _this = this; this.lastSearch = null; this.debouncer = null; this.playlist = new Playlist(); this.player = new Player(this.playlist); this.main = document.getElementsByTagName('main').item(0); // set up htmx event handlers document.addEventListener('htmx:historyRestore', function () { return _this.wireLoadedFragment(); }); document.getElementsByTagName('main').item(0).addEventListener('htmx:afterSwap', function () { return _this.wireLoadedFragment(); }); } Radiostasis.prototype.wireLoadedFragment = function () { var _this = this; // episode play and queue buttons var episodes = this.main.getElementsByClassName('episode'); var _loop_1 = function (i) { var el = episodes.item(i); var episode = { slug: el.dataset.slug, title: el.getAttribute('title'), file: el.dataset.file, series: { slug: el.dataset.sslug, title: el.dataset.series, cover: el.dataset.cover, }, }; // play button el.getElementsByTagName('a').item(0).addEventListener('click', function (e) { _this.player.playEpisode(episode); e.preventDefault(); }); // queue button el.getElementsByTagName('a').item(1).addEventListener('click', function (e) { _this.playlist.queueEpisode(episode); e.preventDefault(); }); }; for (var i = 0; i < episodes.length; i++) { _loop_1(i); } // series filter input var filter = this.main .getElementsByClassName('filter').item(0); if (filter) { if (this.lastSearch) { filter.value = this.lastSearch; this.lastSearch = null; } var allSeries_1 = this.main.getElementsByTagName('section'); filter.addEventListener('input', function () { if (_this.debouncer) clearTimeout(_this.debouncer); _this.debouncer = setTimeout(function () { _this.lastSearch = filter.value.toLowerCase(); var terms = _this.lastSearch.split(' '); for (var i = 0; i < allSeries_1.length; i++) { var series = allSeries_1.item(i); var match = true; for (var _i = 0, terms_1 = terms; _i < terms_1.length; _i++) { var term = terms_1[_i]; if (term.length > 0 && series.dataset.filter.indexOf(term) < 0) { match = false; break; } } if (match) series.classList.remove('no-match'); else series.classList.add('no-match'); } }, 499); }); } }; Radiostasis.prototype.initialize = function () { var path = location.pathname == '/' ? '/partial/home.html' : "/partial/".concat(location.pathname, ".html"); htmx.ajax('GET', path, 'main'); }; return Radiostasis; }()); document.addEventListener('DOMContentLoaded', function () { // start application new Radiostasis().initialize(); });