improved queue current episode style and behavior
This commit is contained in:
parent
9886781afe
commit
5e14db0db0
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pause-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 3.5A1.5 1.5 0 0 1 7 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5zm5 0A1.5 1.5 0 0 1 12 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 289 B |
|
@ -38,10 +38,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
var Player = /** @class */ (function () {
|
var Player = /** @class */ (function () {
|
||||||
function Player(playlist) {
|
function Player(playlist) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
this.stateChangedHandlers = [];
|
||||||
this.playlist = playlist;
|
this.playlist = playlist;
|
||||||
this.playlist.setPlayEpisodeHandler(function (episode) { return _this.playEpisode(episode); });
|
|
||||||
this.playlist.setNowPlayingEpisodeHandler(function () { return _this.currentEpisode(); });
|
|
||||||
this.playlist.setStopHandler(function () { return _this.stopPlaybackAndResetUi(); });
|
|
||||||
var controls = getOrThrow(document.getElementById('controls'));
|
var controls = getOrThrow(document.getElementById('controls'));
|
||||||
var timeVolume = getOrThrow(document.getElementById('timeVolume'));
|
var timeVolume = getOrThrow(document.getElementById('timeVolume'));
|
||||||
this.nowPlaying = getOrThrow(document.getElementById('nowPlaying'));
|
this.nowPlaying = getOrThrow(document.getElementById('nowPlaying'));
|
||||||
|
@ -94,6 +92,9 @@ var Player = /** @class */ (function () {
|
||||||
_this.skipButton.disabled = !_this.playlist.hasNextEpisode();
|
_this.skipButton.disabled = !_this.playlist.hasNextEpisode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Player.prototype.addStateChangedHandler = function (handler) {
|
||||||
|
this.stateChangedHandlers.push(handler);
|
||||||
|
};
|
||||||
Player.prototype.currentEpisode = function () {
|
Player.prototype.currentEpisode = function () {
|
||||||
return this.episode;
|
return this.episode;
|
||||||
};
|
};
|
||||||
|
@ -135,10 +136,18 @@ var Player = /** @class */ (function () {
|
||||||
onplay: function () {
|
onplay: function () {
|
||||||
_this.setPauseButtonUI();
|
_this.setPauseButtonUI();
|
||||||
_this.startTicker();
|
_this.startTicker();
|
||||||
|
for (var _i = 0, _a = _this.stateChangedHandlers; _i < _a.length; _i++) {
|
||||||
|
var handler = _a[_i];
|
||||||
|
handler();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onpause: function () {
|
onpause: function () {
|
||||||
_this.setPlayButtonUI();
|
_this.setPlayButtonUI();
|
||||||
_this.stopTicker();
|
_this.stopTicker();
|
||||||
|
for (var _i = 0, _a = _this.stateChangedHandlers; _i < _a.length; _i++) {
|
||||||
|
var handler = _a[_i];
|
||||||
|
handler();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onend: function () { return _this.nextEpisode(); },
|
onend: function () { return _this.nextEpisode(); },
|
||||||
onloaderror: function () { return _this.setErrorUI('Error playing episode.'); },
|
onloaderror: function () { return _this.setErrorUI('Error playing episode.'); },
|
||||||
|
@ -157,6 +166,38 @@ var Player = /** @class */ (function () {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
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.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();
|
||||||
|
for (var _i = 0, _b = this.stateChangedHandlers; _i < _b.length; _i++) {
|
||||||
|
var handler = _b[_i];
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Player.prototype.nextEpisode = function () {
|
||||||
|
var next = this.playlist.nextEpisode();
|
||||||
|
if (next)
|
||||||
|
this.playEpisode(next);
|
||||||
|
else {
|
||||||
|
this.stopPlaybackAndResetUi();
|
||||||
|
// manually trigger playlist changed
|
||||||
|
// so that currently playing media gets wiped
|
||||||
|
this.playlist.triggerPlaylistChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
Player.prototype.setErrorUI = function (message) {
|
Player.prototype.setErrorUI = function (message) {
|
||||||
this.stopPlaybackAndResetUi();
|
this.stopPlaybackAndResetUi();
|
||||||
Toastify({
|
Toastify({
|
||||||
|
@ -171,12 +212,6 @@ var Player = /** @class */ (function () {
|
||||||
},
|
},
|
||||||
}).showToast();
|
}).showToast();
|
||||||
};
|
};
|
||||||
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 () {
|
Player.prototype.rewind = function () {
|
||||||
if (this.howl && this.howl.state() === 'loaded') {
|
if (this.howl && this.howl.state() === 'loaded') {
|
||||||
var newPos = this.howl.seek() - 10;
|
var newPos = this.howl.seek() - 10;
|
||||||
|
@ -195,17 +230,6 @@ var Player = /** @class */ (function () {
|
||||||
this.updateTimeUI();
|
this.updateTimeUI();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Player.prototype.nextEpisode = function () {
|
|
||||||
var next = this.playlist.nextEpisode();
|
|
||||||
if (next)
|
|
||||||
this.playEpisode(next);
|
|
||||||
else {
|
|
||||||
this.stopPlaybackAndResetUi();
|
|
||||||
// manually trigger playlist changed
|
|
||||||
// so that currently playing media gets wiped
|
|
||||||
this.playlist.triggerPlaylistChanged();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Player.prototype.setPlayButtonUI = function () {
|
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');
|
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');
|
||||||
};
|
};
|
||||||
|
@ -305,31 +329,18 @@ var Player = /** @class */ (function () {
|
||||||
this.ticker = null;
|
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;
|
return Player;
|
||||||
}());
|
}());
|
||||||
var Playlist = /** @class */ (function () {
|
var Playlist = /** @class */ (function () {
|
||||||
function Playlist() {
|
function Playlist(playerDelegate) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
this.queueExpandedHeight = 'calc(100% - 6ex)';
|
this.queueExpandedHeight = 'calc(100% - 6ex)';
|
||||||
// event handlers
|
// event handlers
|
||||||
this.changedHandlers = [];
|
this.changedHandlers = [];
|
||||||
this.nowPlayingEpisodeHandler = null;
|
|
||||||
this.stopHandler = null;
|
|
||||||
this.playEpisodeHandler = null;
|
|
||||||
// the actual episode queue
|
// the actual episode queue
|
||||||
this.episodes = [];
|
this.episodes = [];
|
||||||
this.episodeHash = new Set();
|
this.episodeHash = new Set();
|
||||||
|
this.playerDelegate = playerDelegate;
|
||||||
this.queueContainer = getOrThrow(document.getElementById('queue-container'));
|
this.queueContainer = getOrThrow(document.getElementById('queue-container'));
|
||||||
this.queueInitialHeight = getComputedStyle(this.queueContainer).height;
|
this.queueInitialHeight = getComputedStyle(this.queueContainer).height;
|
||||||
this.queueTab = getOrThrow(this.queueContainer.getElementsByTagName('h2').item(0));
|
this.queueTab = getOrThrow(this.queueContainer.getElementsByTagName('h2').item(0));
|
||||||
|
@ -340,20 +351,15 @@ var Playlist = /** @class */ (function () {
|
||||||
this.queueTab.addEventListener('click', function () { return _this.toggleQueueUI(); });
|
this.queueTab.addEventListener('click', function () { return _this.toggleQueueUI(); });
|
||||||
this.overlay.addEventListener('click', function () { return _this.toggleQueueUI(); });
|
this.overlay.addEventListener('click', function () { return _this.toggleQueueUI(); });
|
||||||
this.clearButton.addEventListener('click', function () { return _this.clearPlaylist(); });
|
this.clearButton.addEventListener('click', function () { return _this.clearPlaylist(); });
|
||||||
this.unshiftCurrent();
|
|
||||||
}
|
}
|
||||||
|
Playlist.prototype.initialize = function () {
|
||||||
|
var _this = this;
|
||||||
|
this.playerDelegate().addStateChangedHandler(function () { return _this.stateChanged(); });
|
||||||
|
this.unshiftCurrent();
|
||||||
|
};
|
||||||
Playlist.prototype.addPlaylistChangedHandler = function (handler) {
|
Playlist.prototype.addPlaylistChangedHandler = function (handler) {
|
||||||
this.changedHandlers.push(handler);
|
this.changedHandlers.push(handler);
|
||||||
};
|
};
|
||||||
Playlist.prototype.setPlayEpisodeHandler = function (handler) {
|
|
||||||
this.playEpisodeHandler = handler;
|
|
||||||
};
|
|
||||||
Playlist.prototype.setNowPlayingEpisodeHandler = function (handler) {
|
|
||||||
this.nowPlayingEpisodeHandler = handler;
|
|
||||||
};
|
|
||||||
Playlist.prototype.setStopHandler = function (handler) {
|
|
||||||
this.stopHandler = handler;
|
|
||||||
};
|
|
||||||
Playlist.prototype.hasNextEpisode = function () {
|
Playlist.prototype.hasNextEpisode = function () {
|
||||||
return this.episodes.length > 0;
|
return this.episodes.length > 0;
|
||||||
};
|
};
|
||||||
|
@ -383,9 +389,7 @@ var Playlist = /** @class */ (function () {
|
||||||
};
|
};
|
||||||
Playlist.prototype.unshiftEpisodes = function (episodes) {
|
Playlist.prototype.unshiftEpisodes = function (episodes) {
|
||||||
this.shiftCurrent();
|
this.shiftCurrent();
|
||||||
var nowPlaying = this.nowPlayingEpisodeHandler
|
var nowPlaying = this.playerDelegate().currentEpisode();
|
||||||
? this.nowPlayingEpisodeHandler()
|
|
||||||
: null;
|
|
||||||
if (nowPlaying) {
|
if (nowPlaying) {
|
||||||
var litem = this.createQueueListItem(nowPlaying);
|
var litem = this.createQueueListItem(nowPlaying);
|
||||||
this.episodes.unshift([nowPlaying, litem]);
|
this.episodes.unshift([nowPlaying, litem]);
|
||||||
|
@ -429,10 +433,8 @@ var Playlist = /** @class */ (function () {
|
||||||
};
|
};
|
||||||
Playlist.prototype.clearPlaylist = function () {
|
Playlist.prototype.clearPlaylist = function () {
|
||||||
var removed = this.episodes.length;
|
var removed = this.episodes.length;
|
||||||
if (this.nowPlayingEpisodeHandler &&
|
if (this.playerDelegate().currentEpisode() != null) {
|
||||||
this.nowPlayingEpisodeHandler() != null) {
|
this.playerDelegate().stopPlaybackAndResetUi();
|
||||||
if (this.stopHandler)
|
|
||||||
this.stopHandler();
|
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
this.episodes.length = 0;
|
this.episodes.length = 0;
|
||||||
|
@ -445,9 +447,7 @@ var Playlist = /** @class */ (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Playlist.prototype.reQueueNowPlaying = function () {
|
Playlist.prototype.reQueueNowPlaying = function () {
|
||||||
var currentEpisode = this.nowPlayingEpisodeHandler
|
var currentEpisode = this.playerDelegate().currentEpisode();
|
||||||
? this.nowPlayingEpisodeHandler()
|
|
||||||
: null;
|
|
||||||
if (currentEpisode) {
|
if (currentEpisode) {
|
||||||
this.unshiftEpisodes([currentEpisode]);
|
this.unshiftEpisodes([currentEpisode]);
|
||||||
}
|
}
|
||||||
|
@ -464,10 +464,12 @@ var Playlist = /** @class */ (function () {
|
||||||
var _a;
|
var _a;
|
||||||
(_a = this.queueList.firstChild) === null || _a === void 0 ? void 0 : _a.remove();
|
(_a = this.queueList.firstChild) === null || _a === void 0 ? void 0 : _a.remove();
|
||||||
};
|
};
|
||||||
|
Playlist.prototype.stateChanged = function () {
|
||||||
|
this.shiftCurrent();
|
||||||
|
this.unshiftCurrent();
|
||||||
|
};
|
||||||
Playlist.prototype.unshiftCurrent = function () {
|
Playlist.prototype.unshiftCurrent = function () {
|
||||||
var currentEpisode = this.nowPlayingEpisodeHandler
|
var currentEpisode = this.playerDelegate().currentEpisode();
|
||||||
? this.nowPlayingEpisodeHandler()
|
|
||||||
: null;
|
|
||||||
if (currentEpisode) {
|
if (currentEpisode) {
|
||||||
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
|
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
|
||||||
}
|
}
|
||||||
|
@ -499,9 +501,9 @@ var Playlist = /** @class */ (function () {
|
||||||
this.queueTab.classList.remove('expanded');
|
this.queueTab.classList.remove('expanded');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Playlist.prototype.createQueueListItem = function (episode, withControls) {
|
Playlist.prototype.createQueueListItem = function (episode, queueControls) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
if (withControls === void 0) { withControls = true; }
|
if (queueControls === void 0) { queueControls = true; }
|
||||||
var item = document.createElement('li');
|
var item = document.createElement('li');
|
||||||
item.classList.add('episode');
|
item.classList.add('episode');
|
||||||
item.title = episode.title;
|
item.title = episode.title;
|
||||||
|
@ -513,25 +515,57 @@ var Playlist = /** @class */ (function () {
|
||||||
aside.appendChild(sspan);
|
aside.appendChild(sspan);
|
||||||
item.appendChild(label);
|
item.appendChild(label);
|
||||||
item.appendChild(aside);
|
item.appendChild(aside);
|
||||||
if (withControls) {
|
if (queueControls) {
|
||||||
|
// play now/remove buttons
|
||||||
var controls = document.createElement('div');
|
var controls = document.createElement('div');
|
||||||
controls.classList.add('controls');
|
controls.classList.add('controls');
|
||||||
var playBtn = document.createElement('a');
|
var playBtn = document.createElement('a');
|
||||||
playBtn.href = '#';
|
playBtn.href = '#';
|
||||||
playBtn.innerHTML = 'Play Now';
|
playBtn.innerHTML = 'Play Now';
|
||||||
playBtn.addEventListener('click', function () {
|
playBtn.addEventListener('click', function (e) {
|
||||||
_this.reQueueNowPlaying();
|
_this.reQueueNowPlaying();
|
||||||
if (_this.playEpisodeHandler)
|
_this.playerDelegate().playEpisode(episode);
|
||||||
_this.playEpisodeHandler(episode);
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
var remBtn = document.createElement('a');
|
var remBtn = document.createElement('a');
|
||||||
remBtn.href = '#';
|
remBtn.href = '#';
|
||||||
remBtn.innerHTML = 'Remove';
|
remBtn.innerHTML = 'Remove';
|
||||||
remBtn.addEventListener('click', function () { return _this.removeEpisode(episode); });
|
remBtn.addEventListener('click', function (e) {
|
||||||
|
_this.removeEpisode(episode);
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
controls.appendChild(playBtn);
|
controls.appendChild(playBtn);
|
||||||
controls.appendChild(remBtn);
|
controls.appendChild(remBtn);
|
||||||
item.appendChild(controls);
|
item.appendChild(controls);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// pause/skip buttons
|
||||||
|
item.classList.add('playing');
|
||||||
|
var controls = document.createElement('div');
|
||||||
|
controls.classList.add('controls');
|
||||||
|
var pauseBtn_1 = document.createElement('a');
|
||||||
|
pauseBtn_1.href = '#';
|
||||||
|
pauseBtn_1.innerHTML = this.playerDelegate().isPlaying() ? 'Pause' : 'Play';
|
||||||
|
if (this.playerDelegate().isPlaying()) {
|
||||||
|
pauseBtn_1.classList.add('pause');
|
||||||
|
}
|
||||||
|
pauseBtn_1.addEventListener('click', function () {
|
||||||
|
_this.playerDelegate().playPause();
|
||||||
|
pauseBtn_1.innerHTML = _this.playerDelegate().isPlaying()
|
||||||
|
? 'Pause'
|
||||||
|
: 'Play';
|
||||||
|
});
|
||||||
|
var skipBtn = document.createElement('a');
|
||||||
|
skipBtn.href = '#';
|
||||||
|
skipBtn.innerHTML = 'Skip';
|
||||||
|
skipBtn.addEventListener('click', function (e) {
|
||||||
|
_this.playerDelegate().nextEpisode();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
controls.appendChild(pauseBtn_1);
|
||||||
|
controls.appendChild(skipBtn);
|
||||||
|
item.appendChild(controls);
|
||||||
|
}
|
||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
Playlist.prototype.notify = function (message) {
|
Playlist.prototype.notify = function (message) {
|
||||||
|
@ -555,8 +589,9 @@ var Radiostasis = /** @class */ (function () {
|
||||||
var _a;
|
var _a;
|
||||||
this.lastSearch = null;
|
this.lastSearch = null;
|
||||||
this.debouncer = null;
|
this.debouncer = null;
|
||||||
this.playlist = new Playlist();
|
this.playlist = new Playlist(function () { return _this.player; });
|
||||||
this.player = new Player(this.playlist);
|
this.player = new Player(this.playlist);
|
||||||
|
this.playlist.initialize();
|
||||||
this.main = getOrThrow(document.getElementsByTagName('main').item(0));
|
this.main = getOrThrow(document.getElementsByTagName('main').item(0));
|
||||||
// set up htmx event handlers
|
// set up htmx event handlers
|
||||||
document.addEventListener('htmx:historyRestore', function () {
|
document.addEventListener('htmx:historyRestore', function () {
|
||||||
|
|
|
@ -606,7 +606,7 @@ h2 svg {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playing .controls {
|
.seriesDetails .playing .controls {
|
||||||
opacity: 0.33;
|
opacity: 0.33;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,6 +614,10 @@ h2 svg {
|
||||||
background-image: url('/icon-play.svg');
|
background-image: url('/icon-play.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.playing .controls a.pause:first-child {
|
||||||
|
background-image: url('/icon-pause.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.controls a:last-child {
|
.controls a:last-child {
|
||||||
background-image: url('/icon-queue.svg');
|
background-image: url('/icon-queue.svg');
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,10 @@ class Player {
|
||||||
// currently playing episode and playlist
|
// currently playing episode and playlist
|
||||||
private episode: Episode | null;
|
private episode: Episode | null;
|
||||||
private playlist: Playlist;
|
private playlist: Playlist;
|
||||||
|
private stateChangedHandlers: Array<() => void> = [];
|
||||||
|
|
||||||
public constructor(playlist: Playlist) {
|
public constructor(playlist: Playlist) {
|
||||||
this.playlist = playlist;
|
this.playlist = playlist;
|
||||||
this.playlist.setPlayEpisodeHandler((episode) => this.playEpisode(episode));
|
|
||||||
this.playlist.setNowPlayingEpisodeHandler(() => this.currentEpisode());
|
|
||||||
this.playlist.setStopHandler(() => this.stopPlaybackAndResetUi());
|
|
||||||
|
|
||||||
const controls = getOrThrow(document.getElementById('controls'));
|
const controls = getOrThrow(document.getElementById('controls'));
|
||||||
const timeVolume = getOrThrow(document.getElementById('timeVolume'));
|
const timeVolume = getOrThrow(document.getElementById('timeVolume'));
|
||||||
|
@ -106,6 +104,10 @@ class Player {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addStateChangedHandler(handler: () => void): void {
|
||||||
|
this.stateChangedHandlers.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
public currentEpisode(): Episode | null {
|
public currentEpisode(): Episode | null {
|
||||||
return this.episode;
|
return this.episode;
|
||||||
}
|
}
|
||||||
|
@ -139,10 +141,16 @@ class Player {
|
||||||
onplay: (): void => {
|
onplay: (): void => {
|
||||||
this.setPauseButtonUI();
|
this.setPauseButtonUI();
|
||||||
this.startTicker();
|
this.startTicker();
|
||||||
|
for (const handler of this.stateChangedHandlers) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onpause: (): void => {
|
onpause: (): void => {
|
||||||
this.setPlayButtonUI();
|
this.setPlayButtonUI();
|
||||||
this.stopTicker();
|
this.stopTicker();
|
||||||
|
for (const handler of this.stateChangedHandlers) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onend: () => this.nextEpisode(),
|
onend: () => this.nextEpisode(),
|
||||||
onloaderror: () => this.setErrorUI('Error playing episode.'),
|
onloaderror: () => this.setErrorUI('Error playing episode.'),
|
||||||
|
@ -160,6 +168,36 @@ class Player {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public playPause(): void {
|
||||||
|
if (this.howl && this.howl.playing()) this.howl.pause();
|
||||||
|
else if (this.howl && !this.howl.playing()) this.howl.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
public stopPlaybackAndResetUi(): void {
|
||||||
|
this.howl?.unload();
|
||||||
|
this.howl = null;
|
||||||
|
this.episode = null;
|
||||||
|
this.setPlayButtonUI();
|
||||||
|
this.updateNowPlayingUI();
|
||||||
|
this.sendMediaSessionMetadata();
|
||||||
|
this.stopTicker();
|
||||||
|
this.updateTimeUI();
|
||||||
|
for (const handler of this.stateChangedHandlers) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public nextEpisode(): void {
|
||||||
|
const next = this.playlist.nextEpisode();
|
||||||
|
if (next) this.playEpisode(next);
|
||||||
|
else {
|
||||||
|
this.stopPlaybackAndResetUi();
|
||||||
|
// manually trigger playlist changed
|
||||||
|
// so that currently playing media gets wiped
|
||||||
|
this.playlist.triggerPlaylistChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setErrorUI(message: string): void {
|
private setErrorUI(message: string): void {
|
||||||
this.stopPlaybackAndResetUi();
|
this.stopPlaybackAndResetUi();
|
||||||
Toastify({
|
Toastify({
|
||||||
|
@ -175,11 +213,6 @@ class Player {
|
||||||
}).showToast();
|
}).showToast();
|
||||||
}
|
}
|
||||||
|
|
||||||
private playPause(): void {
|
|
||||||
if (this.howl && this.howl.playing()) this.howl.pause();
|
|
||||||
else if (this.howl && !this.howl.playing()) this.howl.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private rewind(): void {
|
private rewind(): void {
|
||||||
if (this.howl && this.howl.state() === 'loaded') {
|
if (this.howl && this.howl.state() === 'loaded') {
|
||||||
let newPos = this.howl.seek() - 10;
|
let newPos = this.howl.seek() - 10;
|
||||||
|
@ -198,17 +231,6 @@ class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private nextEpisode(): void {
|
|
||||||
const next = this.playlist.nextEpisode();
|
|
||||||
if (next) this.playEpisode(next);
|
|
||||||
else {
|
|
||||||
this.stopPlaybackAndResetUi();
|
|
||||||
// manually trigger playlist changed
|
|
||||||
// so that currently playing media gets wiped
|
|
||||||
this.playlist.triggerPlaylistChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setPlayButtonUI(): void {
|
private setPlayButtonUI(): void {
|
||||||
this.playButtonPath.setAttribute(
|
this.playButtonPath.setAttribute(
|
||||||
'd',
|
'd',
|
||||||
|
@ -319,15 +341,4 @@ class Player {
|
||||||
this.ticker = null;
|
this.ticker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopPlaybackAndResetUi(): void {
|
|
||||||
this.howl?.unload();
|
|
||||||
this.howl = null;
|
|
||||||
this.episode = null;
|
|
||||||
this.setPlayButtonUI();
|
|
||||||
this.updateNowPlayingUI();
|
|
||||||
this.sendMediaSessionMetadata();
|
|
||||||
this.stopTicker();
|
|
||||||
this.updateTimeUI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,14 @@ class Playlist {
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
private changedHandlers: Array<() => void> = [];
|
private changedHandlers: Array<() => void> = [];
|
||||||
private nowPlayingEpisodeHandler: (() => Episode | null) | null = null;
|
private playerDelegate: () => Player;
|
||||||
private stopHandler: (() => void) | null = null;
|
|
||||||
private playEpisodeHandler: ((episode: Episode) => void) | null = null;
|
|
||||||
|
|
||||||
// the actual episode queue
|
// the actual episode queue
|
||||||
private readonly episodes: Array<[Episode, HTMLLIElement]> = [];
|
private readonly episodes: Array<[Episode, HTMLLIElement]> = [];
|
||||||
private readonly episodeHash: Set<string> = new Set<string>();
|
private readonly episodeHash: Set<string> = new Set<string>();
|
||||||
|
|
||||||
constructor() {
|
constructor(playerDelegate: () => Player) {
|
||||||
|
this.playerDelegate = playerDelegate;
|
||||||
this.queueContainer = getOrThrow(
|
this.queueContainer = getOrThrow(
|
||||||
document.getElementById('queue-container')
|
document.getElementById('queue-container')
|
||||||
);
|
);
|
||||||
|
@ -38,7 +37,10 @@ class Playlist {
|
||||||
this.queueTab.addEventListener('click', () => this.toggleQueueUI());
|
this.queueTab.addEventListener('click', () => this.toggleQueueUI());
|
||||||
this.overlay.addEventListener('click', () => this.toggleQueueUI());
|
this.overlay.addEventListener('click', () => this.toggleQueueUI());
|
||||||
this.clearButton.addEventListener('click', () => this.clearPlaylist());
|
this.clearButton.addEventListener('click', () => this.clearPlaylist());
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
this.playerDelegate().addStateChangedHandler(() => this.stateChanged());
|
||||||
this.unshiftCurrent();
|
this.unshiftCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,18 +48,6 @@ class Playlist {
|
||||||
this.changedHandlers.push(handler);
|
this.changedHandlers.push(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPlayEpisodeHandler(handler: (episode: Episode) => void): void {
|
|
||||||
this.playEpisodeHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setNowPlayingEpisodeHandler(handler: () => Episode | null): void {
|
|
||||||
this.nowPlayingEpisodeHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setStopHandler(handler: () => void): void {
|
|
||||||
this.stopHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public hasNextEpisode(): boolean {
|
public hasNextEpisode(): boolean {
|
||||||
return this.episodes.length > 0;
|
return this.episodes.length > 0;
|
||||||
}
|
}
|
||||||
|
@ -91,9 +81,7 @@ class Playlist {
|
||||||
|
|
||||||
public unshiftEpisodes(episodes: Array<Episode>): void {
|
public unshiftEpisodes(episodes: Array<Episode>): void {
|
||||||
this.shiftCurrent();
|
this.shiftCurrent();
|
||||||
const nowPlaying = this.nowPlayingEpisodeHandler
|
const nowPlaying = this.playerDelegate().currentEpisode();
|
||||||
? this.nowPlayingEpisodeHandler()
|
|
||||||
: null;
|
|
||||||
if (nowPlaying) {
|
if (nowPlaying) {
|
||||||
const litem = this.createQueueListItem(nowPlaying);
|
const litem = this.createQueueListItem(nowPlaying);
|
||||||
this.episodes.unshift([nowPlaying, litem]);
|
this.episodes.unshift([nowPlaying, litem]);
|
||||||
|
@ -142,11 +130,8 @@ class Playlist {
|
||||||
|
|
||||||
public clearPlaylist(): void {
|
public clearPlaylist(): void {
|
||||||
let removed = this.episodes.length;
|
let removed = this.episodes.length;
|
||||||
if (
|
if (this.playerDelegate().currentEpisode() != null) {
|
||||||
this.nowPlayingEpisodeHandler &&
|
this.playerDelegate().stopPlaybackAndResetUi();
|
||||||
this.nowPlayingEpisodeHandler() != null
|
|
||||||
) {
|
|
||||||
if (this.stopHandler) this.stopHandler();
|
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,9 +146,7 @@ class Playlist {
|
||||||
}
|
}
|
||||||
|
|
||||||
public reQueueNowPlaying(): void {
|
public reQueueNowPlaying(): void {
|
||||||
const currentEpisode = this.nowPlayingEpisodeHandler
|
const currentEpisode = this.playerDelegate().currentEpisode();
|
||||||
? this.nowPlayingEpisodeHandler()
|
|
||||||
: null;
|
|
||||||
if (currentEpisode) {
|
if (currentEpisode) {
|
||||||
this.unshiftEpisodes([currentEpisode]);
|
this.unshiftEpisodes([currentEpisode]);
|
||||||
}
|
}
|
||||||
|
@ -181,11 +164,13 @@ class Playlist {
|
||||||
this.queueList.firstChild?.remove();
|
this.queueList.firstChild?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
private unshiftCurrent(): void {
|
private stateChanged(): void {
|
||||||
const currentEpisode = this.nowPlayingEpisodeHandler
|
this.shiftCurrent();
|
||||||
? this.nowPlayingEpisodeHandler()
|
this.unshiftCurrent();
|
||||||
: null;
|
}
|
||||||
|
|
||||||
|
private unshiftCurrent(): void {
|
||||||
|
const currentEpisode = this.playerDelegate().currentEpisode();
|
||||||
if (currentEpisode) {
|
if (currentEpisode) {
|
||||||
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
|
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,7 +204,7 @@ class Playlist {
|
||||||
|
|
||||||
private createQueueListItem(
|
private createQueueListItem(
|
||||||
episode: Episode,
|
episode: Episode,
|
||||||
withControls = true
|
queueControls = true
|
||||||
): HTMLLIElement {
|
): HTMLLIElement {
|
||||||
const item = document.createElement('li');
|
const item = document.createElement('li');
|
||||||
item.classList.add('episode');
|
item.classList.add('episode');
|
||||||
|
@ -232,23 +217,55 @@ class Playlist {
|
||||||
aside.appendChild(sspan);
|
aside.appendChild(sspan);
|
||||||
item.appendChild(label);
|
item.appendChild(label);
|
||||||
item.appendChild(aside);
|
item.appendChild(aside);
|
||||||
if (withControls) {
|
if (queueControls) {
|
||||||
|
// play now/remove buttons
|
||||||
const controls = document.createElement('div');
|
const controls = document.createElement('div');
|
||||||
controls.classList.add('controls');
|
controls.classList.add('controls');
|
||||||
const playBtn = document.createElement('a');
|
const playBtn = document.createElement('a');
|
||||||
playBtn.href = '#';
|
playBtn.href = '#';
|
||||||
playBtn.innerHTML = 'Play Now';
|
playBtn.innerHTML = 'Play Now';
|
||||||
playBtn.addEventListener('click', () => {
|
playBtn.addEventListener('click', (e) => {
|
||||||
this.reQueueNowPlaying();
|
this.reQueueNowPlaying();
|
||||||
if (this.playEpisodeHandler) this.playEpisodeHandler(episode);
|
this.playerDelegate().playEpisode(episode);
|
||||||
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
const remBtn = document.createElement('a');
|
const remBtn = document.createElement('a');
|
||||||
remBtn.href = '#';
|
remBtn.href = '#';
|
||||||
remBtn.innerHTML = 'Remove';
|
remBtn.innerHTML = 'Remove';
|
||||||
remBtn.addEventListener('click', () => this.removeEpisode(episode));
|
remBtn.addEventListener('click', (e) => {
|
||||||
|
this.removeEpisode(episode);
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
controls.appendChild(playBtn);
|
controls.appendChild(playBtn);
|
||||||
controls.appendChild(remBtn);
|
controls.appendChild(remBtn);
|
||||||
item.appendChild(controls);
|
item.appendChild(controls);
|
||||||
|
} else {
|
||||||
|
// pause/skip buttons
|
||||||
|
item.classList.add('playing');
|
||||||
|
const controls = document.createElement('div');
|
||||||
|
controls.classList.add('controls');
|
||||||
|
const pauseBtn = document.createElement('a');
|
||||||
|
pauseBtn.href = '#';
|
||||||
|
pauseBtn.innerHTML = this.playerDelegate().isPlaying() ? 'Pause' : 'Play';
|
||||||
|
if (this.playerDelegate().isPlaying()) {
|
||||||
|
pauseBtn.classList.add('pause');
|
||||||
|
}
|
||||||
|
pauseBtn.addEventListener('click', () => {
|
||||||
|
this.playerDelegate().playPause();
|
||||||
|
pauseBtn.innerHTML = this.playerDelegate().isPlaying()
|
||||||
|
? 'Pause'
|
||||||
|
: 'Play';
|
||||||
|
});
|
||||||
|
const skipBtn = document.createElement('a');
|
||||||
|
skipBtn.href = '#';
|
||||||
|
skipBtn.innerHTML = 'Skip';
|
||||||
|
skipBtn.addEventListener('click', (e) => {
|
||||||
|
this.playerDelegate().nextEpisode();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
controls.appendChild(pauseBtn);
|
||||||
|
controls.appendChild(skipBtn);
|
||||||
|
item.appendChild(controls);
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,9 @@ class Radiostasis {
|
||||||
private debouncer: number | null = null;
|
private debouncer: number | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.playlist = new Playlist();
|
this.playlist = new Playlist(() => this.player);
|
||||||
this.player = new Player(this.playlist);
|
this.player = new Player(this.playlist);
|
||||||
|
this.playlist.initialize();
|
||||||
|
|
||||||
this.main = getOrThrow(document.getElementsByTagName('main').item(0));
|
this.main = getOrThrow(document.getElementsByTagName('main').item(0));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue