improved queue current episode style and behavior

This commit is contained in:
Rudis Muiznieks 2023-04-29 09:27:14 -05:00
parent 9886781afe
commit 5e14db0db0
Signed by: rudism
GPG Key ID: CABF2F86EF7884F9
6 changed files with 205 additions and 134 deletions

3
site/icon-pause.svg Normal file
View File

@ -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

View File

@ -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 () {

View File

@ -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');
} }

View File

@ -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();
}
} }

View File

@ -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;
} }

View File

@ -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));