Compare commits

...

3 Commits

5 changed files with 188 additions and 35 deletions

View File

@ -0,0 +1,11 @@
alter table series
add column date_updated timestamp;
update series
set date_updated='2023-04-15';
pragma writable_schema=on;
update sqlite_master
set sql=replace(sql, 'date_updated timestamp', 'date_updated timestamp not null')
where type='table' and name='series';
pragma writable_schema=off;

View File

@ -96,7 +96,7 @@
<div id='queue-container'>
<h2>Playlist</h2>
<div>
<h3>Up Next:</h3>
<h3>Now Playing:</h3>
<a href='#' id='clear-playlist'>Clear Playlist</a>
</div>
<ol>

View File

@ -40,6 +40,8 @@ var Player = /** @class */ (function () {
var _this = this;
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 timeVolume = getOrThrow(document.getElementById('timeVolume'));
this.nowPlaying = getOrThrow(document.getElementById('nowPlaying'));
@ -161,6 +163,7 @@ var Player = /** @class */ (function () {
text: message,
gravity: 'bottom',
position: 'right',
stopOnFocus: false,
style: {
marginBottom: '10ex',
background: '#a00',
@ -321,6 +324,8 @@ var Playlist = /** @class */ (function () {
this.queueExpandedHeight = 'calc(100% - 6ex)';
// event handlers
this.changedHandlers = [];
this.nowPlayingEpisodeHandler = null;
this.stopHandler = null;
this.playEpisodeHandler = null;
// the actual episode queue
this.episodes = [];
@ -335,6 +340,7 @@ var Playlist = /** @class */ (function () {
this.queueTab.addEventListener('click', function () { return _this.toggleQueueUI(); });
this.overlay.addEventListener('click', function () { return _this.toggleQueueUI(); });
this.clearButton.addEventListener('click', function () { return _this.clearPlaylist(); });
this.unshiftCurrent();
}
Playlist.prototype.addPlaylistChangedHandler = function (handler) {
this.changedHandlers.push(handler);
@ -342,6 +348,12 @@ var Playlist = /** @class */ (function () {
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 () {
return this.episodes.length > 0;
};
@ -370,6 +382,16 @@ var Playlist = /** @class */ (function () {
this.playlistChanged();
};
Playlist.prototype.unshiftEpisodes = function (episodes) {
this.shiftCurrent();
var nowPlaying = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (nowPlaying) {
var litem = this.createQueueListItem(nowPlaying);
this.episodes.unshift([nowPlaying, litem]);
this.episodeHash.add(nowPlaying.id);
this.queueList.prepend(litem);
}
for (var i = episodes.length - 1; i >= 0; i--) {
var episode = episodes[i];
if (this.isQueued(episode))
@ -382,6 +404,7 @@ var Playlist = /** @class */ (function () {
this.notify(episodes.length == 1
? 'Episode added to queue.'
: "".concat(episodes.length, " episodes added to queue."));
this.unshiftCurrent();
this.playlistChanged();
};
Playlist.prototype.removeEpisode = function (episode, ignoreChanged, notify) {
@ -406,20 +429,50 @@ var Playlist = /** @class */ (function () {
};
Playlist.prototype.clearPlaylist = function () {
var removed = this.episodes.length;
if (this.nowPlayingEpisodeHandler &&
this.nowPlayingEpisodeHandler() != null) {
if (this.stopHandler)
this.stopHandler();
removed++;
}
this.episodes.length = 0;
this.episodeHash.clear();
this.queueList.innerHTML = '';
this.unshiftCurrent();
this.playlistChanged();
if (removed > 0) {
this.notify('Playlist cleared.');
}
};
Playlist.prototype.playlistChanged = function () {
this.shiftCurrent();
this.unshiftCurrent();
for (var _i = 0, _a = this.changedHandlers; _i < _a.length; _i++) {
var handler = _a[_i];
handler();
}
};
Playlist.prototype.shiftCurrent = function () {
var _a;
(_a = this.queueList.firstChild) === null || _a === void 0 ? void 0 : _a.remove();
};
Playlist.prototype.unshiftCurrent = function () {
var currentEpisode = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (currentEpisode) {
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
}
else {
var litem = document.createElement('li');
litem.classList.add('episode');
litem.title = 'No episode playing';
var label = document.createElement('label');
label.innerHTML = 'No episode playing';
litem.appendChild(label);
this.queueList.prepend(litem);
}
};
Playlist.prototype.toggleQueueUI = function () {
if (this.queueContainer.style.height !== this.queueExpandedHeight) {
this.queueContainer.style.height = this.queueExpandedHeight;
@ -438,8 +491,17 @@ var Playlist = /** @class */ (function () {
this.queueTab.classList.remove('expanded');
}
};
Playlist.prototype.createQueueListItem = function (episode) {
Playlist.prototype.reQueueNowPlaying = function () {
var currentEpisode = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (currentEpisode) {
this.unshiftEpisodes([currentEpisode]);
}
};
Playlist.prototype.createQueueListItem = function (episode, withControls) {
var _this = this;
if (withControls === void 0) { withControls = true; }
var item = document.createElement('li');
item.classList.add('episode');
item.title = episode.title;
@ -449,24 +511,27 @@ var Playlist = /** @class */ (function () {
sspan.innerHTML = episode.series.title;
var aside = document.createElement('aside');
aside.appendChild(sspan);
var controls = document.createElement('div');
controls.classList.add('controls');
var playBtn = document.createElement('a');
playBtn.href = '#';
playBtn.innerHTML = 'Play Now';
playBtn.addEventListener('click', function () {
if (_this.playEpisodeHandler)
_this.playEpisodeHandler(episode);
});
var remBtn = document.createElement('a');
remBtn.href = '#';
remBtn.innerHTML = 'Remove';
remBtn.addEventListener('click', function () { return _this.removeEpisode(episode); });
controls.appendChild(playBtn);
controls.appendChild(remBtn);
item.appendChild(label);
item.appendChild(aside);
item.appendChild(controls);
if (withControls) {
var controls = document.createElement('div');
controls.classList.add('controls');
var playBtn = document.createElement('a');
playBtn.href = '#';
playBtn.innerHTML = 'Play Now';
playBtn.addEventListener('click', function () {
_this.reQueueNowPlaying();
if (_this.playEpisodeHandler)
_this.playEpisodeHandler(episode);
});
var remBtn = document.createElement('a');
remBtn.href = '#';
remBtn.innerHTML = 'Remove';
remBtn.addEventListener('click', function () { return _this.removeEpisode(episode); });
controls.appendChild(playBtn);
controls.appendChild(remBtn);
item.appendChild(controls);
}
return item;
};
Playlist.prototype.notify = function (message) {
@ -474,6 +539,7 @@ var Playlist = /** @class */ (function () {
text: message,
gravity: 'bottom',
position: 'right',
stopOnFocus: false,
style: {
marginBottom: '10ex',
background: '#090',

View File

@ -26,6 +26,9 @@ class Player {
public constructor(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 timeVolume = getOrThrow(document.getElementById('timeVolume'));
this.nowPlaying = getOrThrow(document.getElementById('nowPlaying'));
@ -163,6 +166,7 @@ class Player {
text: message,
gravity: 'bottom',
position: 'right',
stopOnFocus: false,
style: {
marginBottom: '10ex',
background: '#a00',

View File

@ -10,6 +10,8 @@ class Playlist {
// event handlers
private changedHandlers: Array<() => void> = [];
private nowPlayingEpisodeHandler: (() => Episode | null) | null = null;
private stopHandler: (() => void) | null = null;
private playEpisodeHandler: ((episode: Episode) => void) | null = null;
// the actual episode queue
@ -36,6 +38,8 @@ class Playlist {
this.queueTab.addEventListener('click', () => this.toggleQueueUI());
this.overlay.addEventListener('click', () => this.toggleQueueUI());
this.clearButton.addEventListener('click', () => this.clearPlaylist());
this.unshiftCurrent();
}
public addPlaylistChangedHandler(handler: () => void): void {
@ -46,6 +50,14 @@ class Playlist {
this.playEpisodeHandler = handler;
}
public setNowPlayingEpisodeHandler(handler: () => Episode | null): void {
this.nowPlayingEpisodeHandler = handler;
}
public setStopHandler(handler: () => void): void {
this.stopHandler = handler;
}
public hasNextEpisode(): boolean {
return this.episodes.length > 0;
}
@ -78,6 +90,16 @@ class Playlist {
}
public unshiftEpisodes(episodes: Array<Episode>): void {
this.shiftCurrent();
const nowPlaying = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (nowPlaying) {
const litem = this.createQueueListItem(nowPlaying);
this.episodes.unshift([nowPlaying, litem]);
this.episodeHash.add(nowPlaying.id);
this.queueList.prepend(litem);
}
for (let i = episodes.length - 1; i >= 0; i--) {
const episode = episodes[i];
if (this.isQueued(episode)) this.removeEpisode(episode, true, false);
@ -91,6 +113,7 @@ class Playlist {
? 'Episode added to queue.'
: `${episodes.length} episodes added to queue.`
);
this.unshiftCurrent();
this.playlistChanged();
}
@ -118,10 +141,19 @@ class Playlist {
}
public clearPlaylist(): void {
const removed = this.episodes.length;
let removed = this.episodes.length;
if (
this.nowPlayingEpisodeHandler &&
this.nowPlayingEpisodeHandler() != null
) {
if (this.stopHandler) this.stopHandler();
removed++;
}
this.episodes.length = 0;
this.episodeHash.clear();
this.queueList.innerHTML = '';
this.unshiftCurrent();
this.playlistChanged();
if (removed > 0) {
this.notify('Playlist cleared.');
@ -129,11 +161,35 @@ class Playlist {
}
private playlistChanged(): void {
this.shiftCurrent();
this.unshiftCurrent();
for (const handler of this.changedHandlers) {
handler();
}
}
private shiftCurrent(): void {
this.queueList.firstChild?.remove();
}
private unshiftCurrent(): void {
const currentEpisode = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (currentEpisode) {
this.queueList.prepend(this.createQueueListItem(currentEpisode, false));
} else {
const litem = document.createElement('li');
litem.classList.add('episode');
litem.title = 'No episode playing';
const label = document.createElement('label');
label.innerHTML = 'No episode playing';
litem.appendChild(label);
this.queueList.prepend(litem);
}
}
private toggleQueueUI(): void {
if (this.queueContainer.style.height !== this.queueExpandedHeight) {
this.queueContainer.style.height = this.queueExpandedHeight;
@ -152,7 +208,19 @@ class Playlist {
}
}
private createQueueListItem(episode: Episode): HTMLLIElement {
private reQueueNowPlaying(): void {
const currentEpisode = this.nowPlayingEpisodeHandler
? this.nowPlayingEpisodeHandler()
: null;
if (currentEpisode) {
this.unshiftEpisodes([currentEpisode]);
}
}
private createQueueListItem(
episode: Episode,
withControls = true
): HTMLLIElement {
const item = document.createElement('li');
item.classList.add('episode');
item.title = episode.title;
@ -162,23 +230,26 @@ class Playlist {
sspan.innerHTML = episode.series.title;
const aside = document.createElement('aside');
aside.appendChild(sspan);
const controls = document.createElement('div');
controls.classList.add('controls');
const playBtn = document.createElement('a');
playBtn.href = '#';
playBtn.innerHTML = 'Play Now';
playBtn.addEventListener('click', () => {
if (this.playEpisodeHandler) this.playEpisodeHandler(episode);
});
const remBtn = document.createElement('a');
remBtn.href = '#';
remBtn.innerHTML = 'Remove';
remBtn.addEventListener('click', () => this.removeEpisode(episode));
controls.appendChild(playBtn);
controls.appendChild(remBtn);
item.appendChild(label);
item.appendChild(aside);
item.appendChild(controls);
if (withControls) {
const controls = document.createElement('div');
controls.classList.add('controls');
const playBtn = document.createElement('a');
playBtn.href = '#';
playBtn.innerHTML = 'Play Now';
playBtn.addEventListener('click', () => {
this.reQueueNowPlaying();
if (this.playEpisodeHandler) this.playEpisodeHandler(episode);
});
const remBtn = document.createElement('a');
remBtn.href = '#';
remBtn.innerHTML = 'Remove';
remBtn.addEventListener('click', () => this.removeEpisode(episode));
controls.appendChild(playBtn);
controls.appendChild(remBtn);
item.appendChild(controls);
}
return item;
}
@ -187,6 +258,7 @@ class Playlist {
text: message,
gravity: 'bottom',
position: 'right',
stopOnFocus: false,
style: {
marginBottom: '10ex',
background: '#090',